二叉树中序遍历和后续遍历详解
学习了很多遍二叉树的遍历但是始终没搞明白,搜了很多视频和资料也觉得讲的不够清楚。 所以专门自己深度学习一波并分享给大家。
以下图为例子
以上图为例子来展示遍历算法。
遍历部分使用c语言的伪代码,c语言java都可以用。
引子:中序遍历
要理解后续遍历,先理解前序和中序;
首先,想象程序里的指针是一个人,从节点开始,往左边走,绕着每个节点,然后从根节点的右边绕回来,这个对于前中后序都一样。
中序遍历(左->根->右的顺序)的思路:
1、一个指针指向root,root首先入栈;
2、指针向左移动,节点一路入栈,直到指针指向最左边节点左边的null;
3、此时,弹出栈顶的节点,也就是指针之前的节点,通过pop后,指针也从null移向它,并且打印里面的数据;
4、这时,让指针指向这个节点的右边;
5、如果这个节点右边有节点,也就是有子树,则回归步骤2;
6、如果这个节点右边没有节点(指向null),则回归步骤3,直接让堆栈再弹出一个节点,也就是指针之前指向的节点,并且打印(栈其实就是记录指针指针走过的路);对于中序遍历,每次pop的时候也就是打印的时候。
7、回到2
//中序遍历函数
void InOrderTraverse(BinTree tree){
//声名一个堆栈,以上图为例子,size可以设为9
Stack s = createStack(size);
//声名一个指针node指向树的root,作用是不改变tree本身
BinTree node = tree;
//开始循环
//1.结束大循环的条件:栈为空;
//注意:由于一开始栈为空,所以加一个条件,node不等于null
while(!s.empty() || node != null){
//这个循环是为了让指针一路向左移并使路过的节点入栈
while(node!=null)
//2.指针指向的节点入栈
s.push(node);
//3.节点入栈后,指针向左边移动
node = node->left;
//注意:最后一个循环node会指向最左边的元素的左边那个null
}
//进入这个if有2个情况:1、假如指针的右边为空,跳过楼上的while
// 2、假如指针走到指针上此停留的地方的最左边的节点
if (!s.empty()) {
//弹出栈顶节点,同时让指针回到上次指向的节点
node = s.pop();
//打印节点内数据
print(node.getData()+"\n");
//指向右子树的根,注意可能为null
node = node.getRight();
}
}
前序遍历思路:和中序一模一样,把打印移动到s.push(node)后面就可以了。
重点:后续遍历
网上看了半天,没什么靠谱的;极少的比较靠谱的,思路都和我不一样,我就按照这个思路继续自己研究了下,最后测试成功。
中序和后续的不一样之处在于,后续遍历需要保证:1、每个节点出栈前检查一下,是否构成出栈条件,也就是左右为空,或者左空右已经出站,或者右空左已经出栈,或者左右都出过栈,这4个情况,这个节点才能出栈;2、节点不要重复入栈;
遍历的办法还是一样,想象指针从开始往左边走,绕着每个节点往左下,然后往右,最后从最右边上来到根节点。
//不同处1:
//给Node节点加一个属性,初始false;true的时候代表该节点已经入过栈,false表示没入过
boolean isPoped = false;
//加入一个新函数,判断节点的左右两边都被弹出过栈或者为空,则表明该节点可以出栈;这个应该有简化版,我再想想。
void canBePoped(BinTree Node){
boolean flag = false;
//如果节点左右为空,则可以出栈,如叶节点
if(node.getLeft()==null && node.getRight()==null){
flag = true;
//如果节点的左为空,右已经出过栈,则可以出栈,如例子中的7
}else if(node.getLeft()==null) {
if(node.getRight().isPoped())
flag = true;
//如果节点的右为空,左已经出过栈,则可以出栈,如例子中的5
}else if(node.getRight()==null){
if(node.getLeft().isPoped())
flag = true;
//如果左右都出过栈,才可以出栈,如例子中的1、2、3
}else if(node.getLeft().isPoped() && node.getRight().isPoped()){
flag = true;
}
return flag;
}
//后续遍历函数
void InOrderTraverse(BinTree tree){
Stack s = createStack(size);
BinTree node = tree;
while(!s.empty() || node != null){
while(node!=null)
//不同处2:如果指针没入过栈,则入栈
if(!node.isPoped()) {
s.push(node);
}
node = node->left;
}
if (!s.empty()) {
//不同处3:peek相当于指向栈顶但不出栈
node = s.peek();
//不同处4:判断节点能否出栈
if(canBePoped(node)){
//设被弹出过为true,表示已经弹出栈
node.isPoped = true;
node = s.pop();
print(node.data)
}
node = node.getRight();
}
}