树的遍历
广度优先遍历(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;
}