二叉树的递归和非递归遍历
二叉树的遍历方式
由于二叉树不是线性结构,所以遍历方式可能有多样,二叉树的遍历方式一共有四种情况:先序遍历,中序遍历,后序遍历,层次遍历。所有的遍历都是从根节点出发
先序遍历
先序遍历的过程为:
- 访问根节点
- 先序遍历其左子树
- 先序遍历其右子树
先序遍历的先序可以理解为根先序,也就是优先访问根节点。这是一个递归定义,先根节点,然后左子树,然后右子树。下面这幅图二叉树
按照定义,这课二叉树先序遍历的结果:5314768
中序遍历
中序遍历的过程为:
- 中序遍历左子树
- 访问根节点
- 中序遍历右子树
中序遍历可以理解为根中序,也就是先访问左右子树,再访问根节点,同样这也是一个递归定义,所以这种遍历一定是左子树元素排在前面。下面这幅图中二叉树
中序遍历结果为:1345678,注意观察,是不是已经排好序了。
后序遍历
后序遍历的过程为:
- 后序遍历其左子树
- 后序遍历其右子树
- 访问根节点
后序遍历可以理解为根后序,也就是先访问左子树,右子树,最后访问根节点,注意这也是一个递归的定义。所以这种遍历根节点一定排在最后。下面这幅图中二叉树
后序遍历的结果为:1436875
遍历的递归实现
按照各种遍历的定义,可以很容易写出递归遍历算法,这里直接写出代码
//先序遍历
void PrePrint(BinTree Root){
if(Root){
printf("%d", Root->Data);
PrePrint(Root->Left);
PrePrint(Root->Right);
}
}
//中序遍历
void InPrint(BinTree Root){
if(Root){
InPrint(Root->Left);
printf("%d", Root->Data);
InPrint(Root->Right);
}
}
//后序遍历
void PostPrint(BinTree Root){
if(Root){
PostPrint(Root->Left);
PostPrint(Root->Right);
printf("%d", Root->Data);
}
}
遍历的非递归实现
由于递归函数本身就是用栈实现的,所以我们可以自己使用栈模拟递归函数的写法,然后实现二叉树的遍历。我们使用中序遍历作为例子,讲解非递归遍历二叉树。
中序遍历的非递归实现
想象我们从根节点出发,我们每遇到一个节点都可能是根节点,所以我们遇到的节点得先压栈,一定要左子树遍历完毕,我们才能打印根节点的值。所以,中序遍历的步骤
- 遇到一个节点,就把它压栈,然后遍历左子树
- 当左子树遍历结束后,就可以弹出一个元素并访问它
- 然后右子树同样使用中序遍历算法遍历
//中序遍历的非递归实现
void InPrintWithStack(BinTree Root){
BinTree T = Root;
Stack S = initStack();
//只要树,或者栈不为空,就一直做这个操作
while(T || !isEmpty(S)){
while(T){
//一直向左走,直到底
push(S, T);
T = T->Left;
}
if(!isEmpty(S)){
//弹出一个元素
T = pop(S);
printf("%d", T->Data);
T = T->Right;//结尾一定是这句话,右子树重新来一遍循环
}
}
}
使用下图的二叉树为例,我们演算一遍中序遍历的非递归实现。
- 首先到5节点,注意中序遍历不能访问,压栈
- 继续向左走,遇到3节点,中序遍历不能访问,压栈
- 继续向左走,遇到1节点,中序遍历不能访问,压栈
- 继续向左走,为空了,说明到底了,弹出一个元素,就是1号节点,然后打印1,并向右走(遍历右子树)
- 1号节点向右也没有元素了。这一轮遍历结束
- 再弹出一个元素,3号节点,打印3,然后转向右子树遍历
- 在右子树种,打印4
- 右子树结束之后,再次出栈,打印5,然后又转向又子树
前序遍历的非递归实现
参照中序遍历的非递归实现,其实中序遍历中节点的访问顺序就是前序遍历需要的顺序,所以,前序遍历只要碰到节点就访问它,最终结果就是前序遍历的结果
void PrePrint2(BinTree Root){
BinTree T = Root;
Stack S = initStack();
//二叉树或者栈不为空,就一直做这个操作
while(T || !isEmpty(S)){
//一直向左走,到头
while(T){
push(S, T);
printf("%d", T->Data);//碰到元素就打印,这就是前序遍历的顺序
T = T->Left;
}
if(!isEmpty(S)){
T = pop(S);
T = T->Right;//结尾一定要有T赋值右子树,右子树重新来一遍上面的操作
}
}
}
后序遍历的非递归实现
三种非递归遍历中,属后续遍历最复杂。仔细想一下,它为什么复杂。待续...