二叉树中序遍历和后续遍历详解

二叉树中序遍历和后续遍历详解

学习了很多遍二叉树的遍历但是始终没搞明白,搜了很多视频和资料也觉得讲的不够清楚。 所以专门自己深度学习一波并分享给大家。

以下图为例子

链接: [link](https://imgconvert.csdnimg.cn/aHR0cHM6Ly93d3cuY3Nkbi5uZXQv?x-oss-process=image/format,png).[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mWPb3tjj-1584971019058)(https://pics5.baidu.com/feed/962bd40735fae6cd60694d6afc350d2243a70f6c.jpeg?token=3fa442db78561365d9f2c3e369061855&s=78203C726B426F5B08D830D2000060B3#pic_center)]

以上图为例子来展示遍历算法。
遍历部分使用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();
        }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值