1、二叉树的遍历
》》 1)、所谓二叉树的遍历,是指按某条搜索路径访问树中的每个结点,使得每个结点均被访问
一次,而且仅仅被访问一次。
2)、由二叉树的递归定义可知,遍历一棵二叉树便要决定对“ 根结点 N ”、“ 左子树 L ” 、
“ 右子树 R ” 的访问顺序。
3)、按照 “ 先遍历左子树再遍历右子树 ” 的原则,常见的遍历次序:先序(NLR)、
中序(LNR)、后序(LRN)三种遍历算法。其中,“ 序 ” 指的是根结点在何时被访问。
4)、先序遍历
、 ## 二叉树的图例:
## 先序遍历的操作过程为:
如果二叉树为空,什么也不做。否则:
a. 访问根节点
b. 先序遍历左子树
c. 先序遍历右子树
## 对应的递归算法如下:
void PreOrder(BiTree T){
if( T != NULL ){
visit(T); // 访问根结点
PreOrder( T->lchild ); // 递归遍历左子树
PreOrder( T->rchild ); // 递归遍历右子树
}
}
上图中的二叉树图例,使用先序遍历得到的结果序列为: 1 2 4 6 3 5
5)、中序遍历
## 二叉树的图例
## 中序遍历的操作过程为
如果二叉树为空,什么也不做。否则:
a. 中序遍历左子树
b. 访问根结点
c. 中序遍历右子树
## 对应的递归算法如下:
void InOrder( BiTree T){
if ( T != NULL){
InOrder( T-> lchild ); // 递归遍历左子树
Visit(T); // 访问根结点
InOrder( T-> rchild ); // 递归遍历右子树
}
}
上图中的二叉树图例,使用先序遍历得到的结果序列为: 2 6 4 1 3 5
6)、后序遍历
## 二叉树的图例:
## 后序遍历的操作过程为:
如果二叉树为空,什么也不做。否则:
a. 后序遍历左子树
b. 后序遍历右子树
c. 访问根结点
## 对应的递归算法如下:
void PostOrder( BiTree T){
if( T != NULL ){
PostOrder( T-> lchild ); // 递归遍历左子树
PostOrder( T-> rchild ); // 递归遍历右子树
visit(T); // 访问根结点
}
}
上图中的二叉树图例,使用先序遍历得到的结果序列为: 6 4 2 5 3 1
补充:
三种遍历算法中递归遍历左、右子树的顺序都是固定的,只是访问根结点的顺序不同。不管采用
哪种遍历算法,每个结点都访问一次且仅访问一次,故时间复杂度为 O( n ) 。
在递归遍历中,递归工作栈的栈深恰好为树的深度,所以在最坏的情况下,二叉树是有 n 个结点
且深度为 n 的单支树,遍历算法的空间复杂度为 O( n ) 。
7)、递归算法和非递归算法的转换
*** 可以借助栈,将二叉树的递归遍历算法转换为非递归算法。
8)、层次遍历
*** 如下图所示,二叉树的层次遍历,即按照箭头所指的方向,按照 1 、2 、 3 、 4 的层次顺序,
对二叉树中各个结点进行访问。
*** 要进行层次遍历需要借助一个队列。
先将二叉树根结点入队,,然后出队,访问该结点,如果它有左子树,则将左子树
根结点入队;如果它有右子树,则将右子树结点入队。然后出队,对出队结点访问,
如此反复,直到队列为空。
9)、注意:遍历是二叉树各种操作的基础,可以在遍历的过程中对结点进行各种操作。例如,
对于一棵已知树求结点的双亲、求结点的孩子结点、求二叉树的深度、求二叉树的叶子
个数、判断两棵二叉树是否相同等。所有这些操作都建立在二叉树遍历的基础上,因此,
必须掌握二叉树的各种遍历过程,并能灵活运用以解决各种问题。
10)、由遍历序列构造二叉树
**** 由二叉树的“ 先序序列 ” 和 “ 中序序列 ” 可以唯一地确定一棵二叉树。
**** 由二叉树的 “ 后序序列 ”和 “ 中序序列 ” 可以唯一地确定一棵二叉树。
**** 由二叉树的 “ 层序序列 ”和 “ 中序序列 ” 可以唯一地确定一棵二叉树。
2、线索二叉树
》》线索二叉树的概念
*** 遍历二叉树就是以一定的规则将二叉树中结点排列成一个线性序列,从而得到二叉树
结点的各种遍历序列。
*** 遍历二叉树的实质:对一个非线程结构进行线性化操作,使得在这个访问序列中每一个
结点(除第一个和最后一个)都有一个直接前驱和直接后继。
*** 引入“ 线索二叉树 ”是为了加快查找结点前驱和后继的速度。
*** 在二叉树线索化时,通常规定:若无左子树,令 lchild 指向其前驱结点;若无右子树,
令 rchild 指向后继结点。还需要增加两个标志域表明当前指针域所指对象是指向左(右)
子结点还是直接前驱(后继)。
其中标志域含义如下:
*** 线索二叉树的存储结构描述如下:
typeof struct ThreadNode{
ElemType data; // 数据元素
struct ThreadNode *lchild , *rchild; // 左、右孩子指针
int ltag , rtag ; // 左、右线索标识
}ThreadNode,*ThreadTree;
补充1:以上面这种结点结构构成的二叉链表作为二叉树的存储结构,叫做“ 线索链表 ”。
补充2: 在“ 线索链表 ”中指向结点的前驱和后继的指针,叫做“ 线索 ”。
补充3: 加上线索的二叉树称为“ 线索二叉树 ”
补充4: 对二叉树以某种次序遍历使其变为线索二叉树的过程叫做“ 线索化 ”。
》》线索二叉树的构造
**** 对二叉树的线索化,实质上是遍历一次二叉树,只是在遍历的过程中,检查当前结点左、右
指针域是否为空,若为空,将它们改为指向前驱结点或者后继结点的线索。
》》 线索二叉树的遍历
**** 中序线索化二叉树主要是为了访问运算服务的,这种遍历不再需要借助栈,因为它的结点中
隐藏了线索二叉树的前驱和后继信息。利用线索二叉树,可以实现二叉树遍历的非递归算法。