- 先序遍历
先序遍历过程:
1.访问根节点(输出其值)
2.持续先序遍历其左子树,直到没有左子树后,退回上一节点
3.先序遍历其右子树一次,再回到2
不难看出这是类似走迷宫并且还有退回的一个方法,所以不难想到用递归来做。
递归实现:
每走到一个新的点就将其输出,否则退回上一层的函数中
图例:
从A开始,先输出A,再往左走,输出B,继续走输出D。
D之后的D->Left == NULL,所以跳过if判断,此次调用结束,接下来到了D的->right,此时也是NULL,再次返回到D结点,由于两条路都走不通,所以回到B层,B层的->left已经结束,所以到下一条Right上,即走到了F。
输出F,继续按照顺序先left,输出E,后来E与D的情况一致,回到F,F由于F->right==NULL,因此继续退回到B,由于B的左右两边都已经调用过了,所以退回到A,在A开始进行A->right的步骤,右边不多赘述。
因此到最后输出的顺序应该为
A (B D F E) (C G H I)
- 中序遍历
遍历过程为:
1.持续中序遍历其左子树,当左子树为空时进入2
2.访问根节点(输出其值)
3.中序遍历其右子树一次,再回到1
代码和先序遍历几乎一样,主要就是访问根节点的顺序发生了变化
图例:
从A开始,一直往左走,直到D往左走不动后,输出D
D再往右走,往右走也走不动了,返回到B,输出B
B再往右走,遇到了F
F开始往左走,走到E
E往左走走不动,输出E
往右走走不动,返回到F
输出F,接下来F准备往右走,发现走不动,返回到B
B两边都已经走过了,返回到A并输出A,右边不多赘述
主旨:先往左走,走不动了输出,再往右走
最后的输出顺序为:
(DBEF) A (GHCI)
- 后序遍历
遍历过程为:
1.持续后序遍历其左子树,走不动了到2
2.持续遍历其右子树,走不动了到3
3.访问根节点(输出其值)
言简意赅:完全走不动了就输出这个值并返回上一个结点
先A一直往左走直到D
D往右走走不动,输出D,并返回到B
B往左走走不动,往右走到F,F往左走到E,E左右都走不动,输出E,并返回到F
F往右走走不动,输出F,返回B
B左右两边都走过了,输出B,返回A
A往右走到C,C开始往左走到G,G往左走走不动,往右走到H,H两边都走不动,输出H,返回G
G两边都走完了,输出G,返回C
C往右走到I,I两边走不动,输出I,返回C
C两边都走过了,输出C,返回A,A两边都走过了,递归结束。
因此输出结果为:
(D E F B)(H G I C) A
小总结:先序、中序、后续的遍历路径都是相同的,唯一不同的是访问各节点的时间,导致最后的结果也会有不同
先序——第一次碰到它就输出,最先访问树根
中序——第二次碰到它就输出,在中间位置访问到树根
后续——第三次碰到它就输出,最后访问到树根
-
中序遍历,先序遍历的非递归算法实现:
基本思路:使用堆栈
每走到一个节点,将这个节点入栈,然后到左边走不动了,抛出并访问它
起点开始,A放入堆栈,往B走
B放入堆栈,往D走
D放入堆栈,往左走不动,将D抛出,往右走不动,返回到B(此时要访问的节点就是堆栈栈头,即B)
B已经走过左边了(第二次遇到),将B抛出,往右走
F放入堆栈,再往左边走,放入E
E走不动,抛出,回到F,将F抛出,F往右走,走不动,返回到A(此时B已经被抛出了,堆栈中只剩下A)
A抛出,往右走,将C放入,后面不多赘述
这里对A抛出后的情况解释一下:
T指向A节点,然后T=T->Right,向右走
此时T=C节点,在while(T || !IsEmpty(S))中,||表示的是或运算,只要有一个满足true就能继续下去,此时S是空,返回false,但是T!=NULL,因此可以继续。
直到最后I被抛出后,I->Right为空(false),堆栈也为空(!true == false),外循环才结束。
先序遍历的非递归代码几乎相同,只不过访问(输出)的顺序问题,在第一次遇见的时候就输出,所以把printf直接放到Push()的上面就好了。 -
层次遍历
队列实现:遍历从根节点开始,首先将根节点入队,然后执行循环:
节点出队、访问该节点、将其左右儿子入队(没有就算),直到队列为空
口嗨:
A先放到队列里,然后将队头(A)的左右两边(B,C)入队,输出队头并抛出
将队头(B)的左右两边(D,F)入队,输出队头并抛出
将队头(C)的左右两边(G,I)入队,输出队头并抛出
将队头(D)的左右两边(没东西就不入队),输出队头并抛出
直到最后,将H输出并抛出,此时队列元素为空,循环结束
输出顺序为:
A (B C) (D F G I) (E H)
不难看出,输出是一层一层输出的