二叉树的遍历
本文讲述的是二叉树的遍历。
二叉树的遍历分为四种:先序遍历、中序遍历、后序遍历、层次遍历。
先序遍历
遍历过程:
- 访问根节点
- 先序遍历其左子树
- 先序遍历其右子树
void PreOrderTraversal(BinTree BT) { if (BT) { printf("%d",BT->Data); PreOrderTraversal(BT->Left); PreOrderTraversal(BT->Right) } }

先序遍历=> A B D F E C G H I (A BDFE CGHI--->根 左 右)
中序遍历
遍历过程:
- 中序遍历其左子树
- 访问根节点
- 中序遍历其右子树
void InOrderTraversal(BinTree BT) { if (BT) { InOrderTraversal(BT->Left); printf("%d",BT->Data); InOrderTraversal(BT->Right) } }

中序遍历=> D B E F A G H C I (DBEF A GHCI --->左 根 右)
后序遍历
遍历过程:
- 后序遍历其左子树
- 后序遍历其右子树
- 访问根节点
void PostOrderTraversal(BinTree BT) { if (BT) { PostOrderTraversal(BT->Left); PostOrderTraversal(BT->Right); printf("%d",BT->Data); } }

后序遍历顺序=> D E F B H G I C A (DEFB HGIC A --->左 右 根)
先序、中序、后序遍历过程:遍历过程中经过结点的路线一样,只是访问各节点的时机不同。
图中在从入口到出口的曲线上用叉号、星号和三角符三种符号分别标记出先序、中序、后序访问各节点的时刻。对于根节点来说,如果第一次碰到根节点就print,那么就是先序遍历,如果第二次碰到才print,就是中序遍历,第三次碰到print就是后序遍历。

二叉树的非递归遍历
非递归中栈的实现和递归实现中栈的情况有区别,非递归中自己实现的栈必须通过pop函数才能访问到栈顶的结点,而函数的递归调用时会自动存储当前函数的状态,包括参数和返回地址,下一层函数结束后会直接返回到上一层函数的状态。
中序遍历非递归遍历算法:
中序遍历如之前所说,在第二次遇到此元素时才会print出来,push是一次遇见,随后将元素压入栈,然后在pop访问时,在print 出来
非递归算法实现的基本思路:使用堆栈
- 遇到一个节点,就把它压栈,并去遍历它的左子树
- 当左子树遍历结束后,从栈顶弹出这个结点并访问它
- 然后按其有指针再去中序遍历该结点的右子树
void InorderTraversal(BinTree BT) { BinTree T=BT; Stack S = CreateStack(MaxSize)/*创建并初始化堆栈S*/ while (T || !IsEmpty(S)) { while(T) { /*一直向左并将沿途结点压入堆栈*/ Push(S,T); T = T->Left; } if (!IsEmpty(S)) { T = Pop(S); /*结点弹出堆栈*/ printf("%s5d",T->Data); /*访问打印结点*/ T = T->Right; /*转向右子树*/ } } }
例题:非递归方法中序遍历下面这颗二叉树,其堆栈操作序列(P代表为push,O代表为pop)是什么?
答:PPOPOOPPOO
先序遍历非递归遍历算法
先序遍历和中序遍历不同,在第一次遇见元素时,就会print出来,所以此代码中将print放在push前面。
void InorderTraversal(BinTree BT) { BinTree T=BT; Stack S = CreateStack(MaxSize)/*创建并初始化堆栈S*/ while (T || !IsEmpty(S)) { while(T) { /*一直向左并将沿途结点压入堆栈*/ printf("%s5d",T->Data); /*访问打印结点*/ Push(S,T); T = T->Left; } if (!IsEmpty(S)) { T = Pop(S); /*结点弹出堆栈*/ T = T->Right; /*转向右子树*/ } } }
那么后序遍历呢?
所以要想在第三次访问时输出结点,就必须在第二次pop访问到之后再将该结点push压入栈,待右子树访问完成后再将该结点pop出来输出。
void InOrderTraversal(BinTree BT){ BinTree T=BT; Elemtype t1=FLAG; Stack S = CreatStrack(MaxSize); While(T || !IsEmpty(S)){ while(T){ Push(S,T); T=T->Left; } if(!IsEmpty(S)){ T=Pop(S); while(T->Right==t1||!T->Right){ t1=T; printf("%5d",T->Data); T=Pop(S); } Push(S,T); T=T->Riht; } } }
层序遍历
一层一层访问树
队列实现:遍历从根结点开始,首先将根节点入队,然后开始执行循环:结点出队、访问该结点、其左右儿子入队

思路是这样:首先从根节点开始,把A放到队列里去,然后开始循环,从队列里抛出元素,然后将其左右儿子放进去,也就是把A抛出来,把B C放进队列里。然后将队列里的第一个元素抛出来,也就是B,然后把B的左右儿子D、F放进去。这样一次循环下去,这样的到的遍历顺序为:A B C D F G I E H
所以总结下来,层序基本过程:先根节点入队,然后:
- 从队列中取出一个元素
- 访问该元素所指结点
- 若钙元素所指结点的左、右孩子结点非空,则将其左右孩子的指针顺序入队
void LevelOrderTraversal(BinTree BT) { Queue Q;BinTree T; if (!BT) return; /*若是空树则直接返回*/ Q = CreateQueue(MaxSize); /*创建并初始化队列Q*/ AddQ(Q,BT); while (!IsEmptyQ(Q)) { T = DeleteQ(Q); printf("%d\n",T->Data ); /*访问取出队列的结点*/ if (T->Left) AddQ(Q,T->Left); if (T->Right) AddQ(Q,T->Right); } }
遍历二叉树的的应用:
1.输出二叉树中的叶结点
在二叉树的先序遍历算法中检测结点的“左右子树是否都为空”。
void PreOrderPrintLeaves(BinTree BT) { if (BT) { if (!BT->Left && !BT->Right) /*左右子树都为空,则为叶结点*/ printf("%d",BT->Data ); PreOrderPrintLeaves(BT->Left); PreOrderPrintLeaves(BT->Right); } }
2.求二叉树的高度
二叉树的高度=左右子树的最大高度+1

利用且修改后序遍历的算法:
int PostOrderGetHight(BinTree BT) { int HL,HR,MaxH; if (BT) { HL = PostOrderGetHight(BT->Left); /*求左子树的深度*/ HR = PostOrderGetHight(BT->Right); /*求右子树的深度*/ MaxH = (HL > HR)?HL:HR; /*取左右子树较大的深度*/ return (MaxH+1); /*返回树的深度*/ } else return 0; /*空树深度为0*/ }
3.由两种遍历序列确定二叉树
由两种遍历序列确定二叉树的前提是必须告诉了你中序遍历。没有中序遍历,我们就不能确定一个唯一的二叉树。比如:

上面图中这两个树,先序遍历顺序都是 A B,后序遍历顺序都是 B A,所以就没办法确定究竟是哪一种。因为我们先序遍历的顺序是“根 左 右”,而后序遍历顺序是“左 右 根”,根容易确定,但是左右的边界并不好确定。
(1)先序和中序遍历序列来确定一棵二叉树:
- 根据先序遍历序列第一个结点确定根节点
- 根据根结点在中序遍历序列中分割出左右两个子序列
- 最左子树和右子树分别递归使用相同的方法继续分解。

例如:已知有颗5个结点的二叉树,其前序遍历序列是a????,中序遍历序列是a????,可以断定:该树根节点是a,且没有左子树。
例:先序序列:a b c d e f g h i j 后序序列: c b e d a h g i j f。
那么我们可以确定a是树的根节点,bcde为左子树的先序遍历顺序,fghij为右子树的先序遍历顺序。如图:
继续以这种方法思考下去,就能得到整个树的结构:
类似的,后序和中序遍历序列也可以确定一棵二叉树。
卡特兰数
一个序列最多可能存在多少种出栈序列?




浙公网安备 33010602011771号