分层次的非线性结构——树(查找问题)04

树的遍历

在这里插入图片描述
广度优先遍历(Breadth-First Search)

1.基于顺序存储结构的树的广度优先遍历
在这里插入图片描述
2.基于链式存储结构的二叉树广度优先遍历
从根结点开始访问,然后以这个结点为线索,顺序访问与之直接相连的结点(孩子)序列,以此序列为线索,重复操作,直至所有结点访问完毕。

在这里插入图片描述

算法描述:
(1)初始化一个队列,并把根结点入列队;
(2)队列元素出队,取得一个结点,访问该结点;
(3)若该结点的左孩子非空,则将该结点的左子树入队列;
(4)若该结点的右孩子非空,则将该结点的右子树入队列;
(5) 循环执行步骤2到4,直至队列为空。

二叉树结点结构描述:

  typedef struct node
  {  
      datatype  data;
      struct node *lchild,*rchild;
  }   bitree;
 bitree *Q[MAXSIZE]; 
//设数组Q做队列,队列元素类型为二叉链表结点类型

程序描述:

/*=====================================
函数功能:层次遍历二叉树,打印遍历序列
函数输入:二叉链表根结点地址bitree *Ptr
函数输出:无
=======================================*/

void levelOrder (bitree *Ptr)  
{
 	bitree 	*s;
?
 	rear=1; front=0;   					//循环队列初始化 
 	Q[rear]= Ptr;      					//根结点入队 
 	if ( Ptr!=NULL ) 					//根结点非空 
 	{ 	
 		while ( front<rear )   			//队列非空, 执行以下操作 
 		{	
 			front= (front+1)% MAXSIZE;
 			Ptr=Q[front];             	//队头元素出队 
 			printf (" %c ", Ptr→data);    	//访问出队结点 
 			if (Ptr→lchild!=NULL)    		//Ptr的左孩子入队 
 			{	 
 				rear=(rear+1) % MAXSIZE;
   				Q[rear]= Ptr→lchild;
 			}
 			if (Ptr→rchild!=NULL) 		//Ptr的右孩子入队 
 			{ 	
 				rear=(rear+1) % MAXSIZE; 
     			Q[rear]= Ptr→rchild;
 			}  
  		}
 	}
} 

深度优先遍历(Depth-First Search)

深度优先遍历方法:
先序遍历(DLR)
中序遍历(LDR)
后序遍历(LRD)

深度优先遍历递归算法:

1.先序遍历的递归算法

DLR递归过程:每次都是先访问根结点,再走树的左分支,直到左子树为空,返回,再访问右子树。
在这里插入图片描述

在这里插入图片描述

程序实现:

/*=================================
函数功能:先序遍历树的递归算法
函数输入:树的根结点
函数输出:无
屏幕输出:树的先序遍历序列
====================================*/
void PreOrder(BinTreeNode  *t) 
{
 	if( t!=NULL )
 	{
		putchar(t->data);
		PreOrder (t->lchild);
		PreOrder (t->rchild);
 	}
}    //先序遍历

2.中序遍历的递归算法

/*=============================
函数功能:中序遍历树的递归算法
函数输入:树的根结点
函数输出:无
屏幕输出:树的中序遍历序列
===============================*/
void  inorder(BinTreeNode *t)
{
    if ( t ) 
    {
         inorder(t->lchild);
         putchar(t->data);  
         inorder(t->rchild);
    }
}

3.后序遍历的递归算法

/*=====================================
    函数功能:后序遍历树的递归算法
    函数输入:树的根结点
    函数输出:无
    屏幕输出:树的后序遍历序列
======================================*/
void  postorder (BinTreeNode *t)
{
    if ( t ) 
    {    postorder(t->lchild);
         postorder(t->rchild);
         putchar(t->data);  
    }
}

用遍历的方法建立二叉链表

解: 算法基本思想,按先序遍历顺序,建立二叉链表的所有结点并完成相应结点的链接。
树的先序序列为ABD@F@@@CE@@@
在这里插入图片描述

/*=========================================
函数功能:用先序遍历的方法建立二叉链表
函数输入:(二叉链表根结点)
函数输出:二叉链表根结点
键盘输入:树的先序遍历序列,子树为空时输入@
============================================*/	  
BinTreeNode *CreatBTree_DLR(BinTreeNode *root )
{   
	char ch;
	scanf("%c",&ch);
	if (ch=='@')  root=NULL; //ch=='@'子树为空,则root=NULL返回
	else  
	{                                                  
		root=( BinTreeNode * )malloc(sizeof(BinTreeNode));//建立结点
	   	root->data = ch;  
		//构造左子树链表,并将左子树根结点指针赋给(根)结点的左孩子域
	   	root->lchild=CreatBTree_DLR(root->lchild);
	   	//构造右子树链表,并将右子树根结点指针赋给(根)结点的右孩子域
	   	root->rchild=CreatBTree_DLR(root->rchild);   
	}
	return (root);        
}

4.深度优先遍历非递归算法

栈是实现递归时最常用的辅助结构,利用一个栈来记录尚待遍历的结点,以备以后访问,可以将递归的深度优先遍历改为非递归的算法。

那么非递归算法比递归算法有什么好处?

1)非递归先序遍历

算法设计:
在这里插入图片描述
遇到一个结点,就访问该结点,并把此结点推入栈中,然后去遍历它的左子树。
遍历完它的左子树后,从栈顶弹出这个结点,并按照它的右链接指示的地址再去遍历该结点的右子树结构。

程序如下:

/*==================================
函数功能:先序遍历树的非递归算法
函数输入:树的根结点
函数输出:无
屏幕输出:树的先序遍历序列
=========================================*/
#define MAX 20
	void PreOrder_NR(BinTreeNode *root) 
	{ 
		BinTreeNode *Ptr;
		BinTreeNode *Stack[MAX]; //栈定义
		int top=0; //栈顶指针
		
		Ptr=root; 
		do 
	   {
		   while( Ptr!=NULL) //树结点非空,遍历其左子树
		   {
			   printf("%c", Ptr->data) ;  //打印结点值
			   Stack[top]=Ptr;  //树结点进栈
			   top++;
			   Ptr=Ptr->lchild;  //查看左子树
		   }
		   if (top>0)  //栈非空,出栈
		   {
			   top--;
			   Ptr=Stack[top]; 
			   Ptr=Ptr->rchild; //取栈顶结点右子树
		   }
		}  while( top>0 || Ptr!=NULL); 
	 }

2)非递归中序遍历
在这里插入图片描述
3)非递归后序遍历
在这里插入图片描述
测试程序:

/*=========================================
测试功能:树的各种遍历算法测试
测试函数:
 	1. 用先序遍历的方法建立二叉链表CreatBTree_DLR
	2. 非递归先序遍历序列PreOrder_NR
	3. 递归先序遍历序列PreOrder
	4. 递归中序遍历序列inorder
	5. 递归后序遍历序列postorder
==========================================*/
#include "stdio.h"
#include <stdio.h>
#include <stdlib.h>
typedef struct node
{ char data;
  struct node *lchild,*rchild;
} BinTreeNode;

int main()
{
	BinTreeNode *RPtr;
	printf("建立树,输入树的先序遍历序列\n");
	RPtr=CreatBTree_DLR(RPtr);
	printf("\n非递归先序遍历序列结果");
	PreOrder_NR(RPtr);
	printf("\n递归先序遍历序列结果");
	PreOrder(RPtr);
	printf("\n递归中序遍历序列结果");
	inorder(RPtr);
	printf("\n递归后序遍历序列结果");
	postorder(RPtr);
	printf("\n");
}

树的遍历的应用

1.求二叉树深度

1)按先序遍历的方式求二叉树深度
二叉树的深度是二叉树中结点层次的最大值。可通过先序遍历来计算二叉树中每个结点的层次, 其中的最大值即为二叉树的深度。

在这里插入图片描述
在这里插入图片描述
该算法会导致根结点右子树求深度不准确

程序实现:

/*===================================================
函数功能:按先序遍历的方式求二叉树深度
函数输入:根结点
函数输出:树的深度
屏幕输出:(叶子结点值、层数、树的当前高度)——方便调试用
=====================================================*/
int  h=0; //全局量累计树的深度
int   TreeDepth_DLR(BinTreeNode *p, int leve ) 
{      
    		if ( p!= NULL) 
		{
			leve++; 
  			if( leve>h )  h=leve; 
			putchar(p->data);
			printf(" leve=%d,h=%d\n",leve,h);
			h=TreeDepth_DLR( p->lchild, leve );  //计算左子树的深度    
			h=TreeDepth_DLR( p->rchild, leve );  //计算右子树的深度  
   		} 
       	return  h; 
} 

2)按后序遍历的方式求二叉树深度
在这里插入图片描述
在这里插入图片描述
把左右子树的高度分别记录在lh、rh两个变量中,即使它们都是局部量,但在每一层二者都是可比较的。

程序实现:

/*================================================
函数功能:按后序遍历的方式求二叉树深度
函数输入:根结点
函数输出:树的深度
屏幕输出:(叶子结点值、左右子树高度)——方便调试用
================================================*/
int  TreeDepth_LRD(BinTreeNode *p )
{  
   if (p!=NULL) 
     {
         int lh = TreeDepth_LRD( p->lchild );
         int rh = TreeDepth_LRD( p->rchild );
         putchar(p->data);
         printf(":lh=%d  rh=%d\n",lh+1,rh+1);
         return lh<rh?  rh+1:  lh+1;
    }
  return 0; 
}

2.统计叶子数目

利用遍历的方式来访问树的各个结点,由于叶子结点的特殊性,因此可以统计出一棵树中叶子的数目。
叶子结点判断条件:
root ->lchild == NULL && root ->rchild==NULL
或者: !root->lchild && !root->rchild

统计二叉树中叶子结点的数目并打印出叶子结点的值。

解:
若结点root的左右指针均空,则为叶子。可选用任何一种遍历算法查找叶子,将其统计并打印出来。

【方法一】用先序遍历递归法求树的叶子结点的数目

/*============================================
函数功能:用先序遍历递归法求树的叶子结点的数目
函数输入:二叉树根结点
函数输出:无(通过全局量传递叶子结点的数目)
屏幕输出:叶子结点值
=============================================*/
int sum=0;//通过全局量传递叶子结点的数目
void LeafNum_DLR(BinTreeNode *root)    
{   if ( root!=NULL )  //非空二叉树条件,等效于 if(root)
	{	if(!root->lchild && !root->rchild)  //是叶子结点则统计并打印
		{   
			sum++;    
			printf("%c  ",root->data);   
		}
		LeafNum_DLR(root->lchild); //递归遍历左子树,直到叶子处
		LeafNum_DLR(root->rchild); //递归遍历右子树,直到叶子处
	} 
}

【方法二】递归求树的叶子结点的数目

/*====================================
函数功能:递归求树的叶子结点的数目
函数输入:根结点地址
函数输出:叶子结点数目
=======================================*/
int LeafNum(BinTreeNode *root ) 
{	if (root ==NULL) return(0);
	else if (root ->lchild==NULL && root ->rchild==NULL)
	          return(1);
	else return(LeafNum(root->lchild)+LeafNum(root->rchild));
}

求叶子结点函数的测试

在这里插入图片描述

/*============================================
测试功能:求叶子结点的数目的测试
测试函数:
1. 递归求树的叶子结点的数目LeafNum 
2. 用先序遍历递归法求树的叶子结点的数目LeafNum_DLR 
===============================================*/
int main()
{
	BinTreeNode *RPtr;
	int i;
	RPtr=creatBtree_DLR(RPtr);
	LeafNum_DLR(RPtr);
	printf("LeafNum_DLR:%d\n ",sum);
	i=LeafNum(RPtr);
	printf("LeafNum:%d \n",i);
	return 0;
}
发布了26 篇原创文章 · 获赞 3 · 访问量 1469

猜你喜欢

转载自blog.csdn.net/herui7322/article/details/104216054
今日推荐