二叉树的遍历
“遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。而二叉树是非线性结构,每个结点都可能有两个后继,则存在如何遍历即按什么样的搜索路径遍历的问题。
二叉树的遍历(traversing binary tree)是指从根结点出发,按照某种次序依次访问二叉树中所有结点,使得每个结点被访问一次且仅被访问一次。
对“二叉树”而言,遍历方式有很多,如果按照思维从左到右的习惯:
- 先序遍历DLR:
–若二叉树为空,则空操作返回,否则先访问根结点,然后前序遍历左子树,再前序遍历右子树。 遍历的顺序为:ABDHIEJCFKG
-
中序遍历LDR:若二叉树为空,则空操作;否则从根结点开始(注意并不是先访问根结点),中序遍历左子树;访问根结点;中序遍历右子树。遍历的顺序为:HDIBEJAFKCG
-
后续遍历LRD:若树为空,则空操作返回,否则从左到右先叶子后结点的方式遍历访问左右子树,最后访问根结点。 遍历的顺序为:HIDJEBKFGCA
-
层序遍历:若树为空,则空操作返回,否则从树的第一层,也就是根结点开始访问,从上而下逐层遍历,在同一层中,按从左到右的顺序对结点逐个访问。 遍历的顺序为:ABCDEFGHIJK
实例:题目要求:建议二叉树并输出每个字符所在的层数。如右图要求输出
A在第一层
B、C在第二层
D、E在第三层
#include<stdio.h>
#include<stdlib.h>
typedef char TElemType;
typedef struct BiTNode
{
TElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
// 创建一棵二叉树,约定遵照先序遍历的方式输入数据
void CreateBiTree(BiTree *T)
{
char c;
scanf("%c", &c);
//用#表示空结点
if('#' == c )
{
*T = NULL;
}
else
{
*T = (BiTNode *)malloc(sizeof(BiTNode));
(*T)->data = c;
CreateBiTree(&(*T)->lchild);
CreateBiTree(&(*T)->rchild);
}
}
void visit(char c,int level)
{
printf("%c 位于第 %d 层\n", c, level);
}
//先序遍历
void PreOrderTraverse(BiTree T,int level)
{
if( T )
{
visit(T->data, level);
PreOrderTraverse(T->lchild, level+1);
PreOrderTraverse(T->rchild, level+1);
}
}
int main()
{
int level = 1;
BiTree T = NULL;
CreateBiTree(&T);
PreOrderTraverse(T, level);
return 0;
}
void
代码中已经写出先序遍历的递归算法,中序遍历及后序遍历只需要修改部分代码位置即可。
- 遍历算法的应用:统计二叉树中叶子结点的个数、求二叉树的深度、先序建立二叉链表、表达式二叉树、根据先序和中序序列构造二叉树等
//统计二叉树中叶子结点的个数
void CountLeaf (BiTree T, int *count){
if ( T ) {
if ((!T->lchild)&& (!T->rchild))
count++; // 对叶子结点计数
CountLeaf( T->lchild, count);
CountLeaf( T->rchild, count);
}
} // CountLeaf
先序遍历二叉树,在遍历过程中查找叶子结点,并计数。由此,需在遍历算法中增添一个“计数”的参数,并将算法中“访问结点”的操作改为:若是叶子,则计数器增1。
//求二叉树深度
int depth( BiTree T ) {
// 本算法返回根指针为T 的二叉树的深度
if ( !T ) return 0;
left = depth(T->lchild ); // 求左子树深度
right = depth(T->rchild ); // 求右子树深度
return left>right ? left+1 : right+1;
}//depth
首先分析二叉树的深度和它的左、右子树深度之间的关系。从二叉树深度的定义可知,二叉树的深度应为其左、右子树深度的最大值加1。由此,需先分别求得左、右子树的深度,算法中“访问结点”的操作为:求得左、右子树深度的最大值,然后加 1 。
线索二叉树
在二叉链表中,若结点数为n,则有2n个指针域,其中,非空指针域 n-1 个,空指针域 n+1 个。
由于 在二叉链表中大约有一半指针域为空; 在二叉链表中进行遍历操作不很方便。
所以为了• 为了使二叉树的遍历操作更加方便、更加有效,利用二叉链表的空指针域存放结点的前驱和后继的信息。
二叉树结点中指向某种遍历次序下的前驱结点或后继结点的指针称之为线索。
加进了线索的二叉链表称为线索链表,其逻辑结构称为线索二叉树。
在线索二叉树中,结点之间存在两种关系:1) 双亲—孩子关系(树形结构中的关系)2) 前驱—后继关系(线性结构中的关系)
线索的标识: 在线索链表中,指针域的值虽然都是地址,但意义不同,线索和孩子结点的地址必须加以区分。为此,需改变结点结构,增加两个标志域
#include<stdio.h>
#include<stdlib.h>
typedef char ElemType;
//枚举类型 (0和1)
//线索存储标志位
//Link(0):表示指向左右孩子的指针
//Thread(1):表示指向前驱后继的线索
typedef enum{Link, Thread} PointerTag;
typedef struct BiThrNode
{
char data;
struct BiThrNode *lchild, *rchild;
PointerTag LTag,RTag;
}BiThrNode, *BiThrTree;
//设置一个全局变量,始终指向刚刚访问过的结点
BiThrTree pre;
//创建一棵二叉树,约定用户遵照先序遍历的方式输入数据
void CreateBiThrTree(BiThrTree *T)
{
char c;
//用空格表示空结点
scanf("%c",&c);
if(' '== c)
{
*T = NULL;
}
else
{
*T =(BiThrNode *)malloc(sizeof(BiThrNode));
(*T)->data = c;
//先默认有左右孩子,之后再将二叉树线索化
(*T)->LTag = Link;
(*T)->RTag = Link;
CreateBiThrTree(&(*T)->lchild);
CreateBiThrTree(&(*T)->rchild);
}
}
//将中序遍历线索化
void InThreading(BiThrTree T)
{
if( T )
{
InThreading(T->lchild); //递归左孩子线索化
//节点处理
if( !T->lchild) //若该结点没有左孩子,则设置LTag为Thread,并把lchild指向刚刚访问过的结点
{
T->LTag = Thread; //前驱
T->lchild = pre; //指向前一个访问的结点
}
if(!pre->rchild) //若前一个访问结点没有右孩子
{
pre->RTag = Thread;
pre->rchild = T;
}//则设置前一个访问结点的后继线索
pre = T;
InThreading(T->rchild);
//递归右孩子线索
}
}
//附设的指针pre
//始终保持指针pre指向当前访问的指针T所指结点的前驱
void InOrderThreading(BiThrTree *p, BiThrTree T)
{
*p = (BiThrTree)malloc(sizeof(BiThrNode));
(*p)->LTag = Link;
(*p)->RTag = Thread;
(*p)->rchild = *p; // 添加头结点
if(!T) //空疏
{
(*p)->lchild = *p;
}
else
{
(*p)->lchild = T; //指向根
pre = *p;
InThreading(T);
pre->rchild = *p; // 处理最后一个结点
pre->RTag = Thread;
(*p)->rchild = pre;
}
}
void visit(char c)
{
printf("%c",c);
}
void InOrderTraverse(BiThrTree T)
{
BiThrTree p;
p = T->lchild; //指向根节点
while(p != T) //空树或遍历结束
{
while(p->LTag == Link)
{
p = p->lchild; //找最左下结点
}
visit(p->data);
while(p->RTag == Thread && p->rchild !=T)//遍历未结束
{
p = p->rchild;
visit(p->data);
}
p = p->rchild;
}
}
int main()
{
BiThrTree P, T = NULL;
CreateBiThrTree( &T );
InOrderThreading( &P, T );
printf("中序遍历输出结果为: ");
InOrderTraverse( P );
printf("\n");
return 0;
}