二叉树的非递归遍历

二叉树是一种经典的数据结构,各大公司面试的时候也喜欢考,我将我所总结积累的二叉树的几种非递归遍历分享一下,欢迎大家提出更多更好的方法:


一、先序遍历

方法一,思想是从根节点一直向左边走,边走边打印,左边走不通了,出栈向右边走,边界条件是current!=null或者栈非空
public void preOrder1(){
		if(root==null) return;
		Node current=root;
		Stack<Node> s=new Stack<Node>();
		while(current!=null||!s.isEmpty()){
			while(current!=null){
				System.out.print(current.data+" ");
				s.push(current);
				current=current.leftchild;
			}
			//中间节点不可以再次向左边走
			current=s.pop().rightchild;
			
		}
		System.out.println();
	}

方法二,使用栈先进后出的特性,简洁,我喜欢。先将中间节点出栈打印,然后依次将右孩子、左孩子入栈,循环该步骤。
public void preOrder2(){
		if(root==null) return;
		Stack<Node> s=new Stack<Node>();
		s.push(root);
		while(!s.isEmpty()){
			Node node=s.pop();
			System.out.print(node.data+" ");
			if(node.rightchild!=null)
				s.push(node.rightchild);
			if(node.leftchild!=null)
				s.push(node.leftchild);
		}
		System.out.println();
	}

二、中序遍历

方法一,类似于先序遍历的方法一,在打印顺序上略有不同
public void inOrder1(){
		if(root==null) return;
		Node current=root;
		Stack<Node> s=new Stack<Node>();
		while(current!=null||!s.isEmpty()){
			while(current!=null){
				
				s.push(current);
				current=current.leftchild;
			}
			Node n=s.pop();
			System.out.print(n.data+" ");
			current=n.rightchild;
			
		}
		System.out.println();
	}
方法二,有时候我们不想使用current!=null||!s.isEmpty()这个边界条件,想用简单一点循环终止条件!s.isEmpty()。
两种方法,一种是在向左搜索和向右搜索时,遇到null也入栈,可以保证即使右孩子为null,栈也非空;另一种是向右搜索时做判断,右孩子为空时向上回溯,非空时将右孩子入栈,这就是下面的这种处理方式:
//PS:如果把null也放入栈中,会使代码简洁一点
	public void inOrder2(){
		Stack<Node> s=new Stack<Node>();
		if(root==null) return;
		s.push(root);
		while(!s.isEmpty()){
			Node n=(Node) s.peek();
			while(n.leftchild!=null){
				n=n.leftchild;
				s.push(n);
				
			}
			//栈非空 且都无右节点 往上回溯
			while(!s.isEmpty()&&((Node) s.peek()).rightchild==null){
				
				System.out.print(((Node)s.pop()).data+" ");
				
			}
			if(!s.isEmpty())
			{
				n=((Node) s.pop());
			
				System.out.print(n.data+" ");
				s.push(n.rightchild);
			}
		}
		System.out.println();
	}

三、后序遍历

后序遍历较前两种复杂一点,主要是由于后序遍历时,要经过中间节点两次,每次经过的我们就要想 是出栈打印呢还是向右搜索呢,如果右孩子为空,当然直接出栈打印,如果右孩子不为空呢,那么我们要进行一个判断,
方法一:可以使用flag标记,第一次由中间节点到达右孩子时,flag=1,第二次由右孩子返回中间节点时,判断flag==1即可知右子树已经遍历完了,将中间节点出栈打印
方法二:记录下前一个节点pre,经过中间节点时判断,当右孩子为空或者cur.rightchild==pre即pre等于右孩子,则将中间节点出栈打印,否则向右搜索右子树
方法三:使用栈先进后出的特性,中间节点peek后先右孩子入栈再左孩子入栈。这里要做处理,记录下前一个节点,当current.leftchild==pre||current.rightchild==pre时,表示中间节点的左右子树已经遍历过了,将中间节点打印出栈,否则就要考虑将右左孩子入栈。注意:该方法开始时并没有一下子向左遍历到左下节点。
下面是上面三种方法的代码:
<span style="white-space:pre">	</span>public void postOrder1(){
		if(root==null) return;
		Stack<Node> s=new Stack<Node>();
		Node cur=root;
		while(cur!=null||!s.isEmpty()){
			while(cur!=null){
				s.push(cur);
				cur=cur.leftchild;
			}
			cur=s.peek();
			if(cur.rightchild==null||cur.flag==1){
				System.out.print(cur.data+" ");
				s.pop();
				cur=null;
			}else{
				cur.flag=1;
				cur=cur.rightchild;
			}
		}
		System.out.println();
	}

<span style="white-space:pre">	</span>public void postOrder2(){
		if(root==null) return;
		Stack<Node> s=new Stack<Node>();
		Node pre=root;
		Node cur=root;
		while(cur!=null||!s.isEmpty()){
			while(cur!=null){
				s.push(cur);
				cur=cur.leftchild;
			}
			cur=s.peek();
			if(cur.rightchild==null||cur.rightchild==pre){
				System.out.print(cur.data+" ");
				pre=cur;
				s.pop();
				//下次循环时为避免重新push cur后重新向左搜索,要释放掉该cur
				cur=null;
			}else{
				cur=cur.rightchild;
			}
		}
		System.out.println();
	}
//和方法二类似,但是用到了栈的特性,故不用先一直搜索到左下节点
	public void postOrder3(){
		if(root==null) return;
		
		Stack<Node> s=new Stack<Node>();
		s.push(root);
		Node pre=root;
		while(!s.isEmpty()){
			Node current=s.peek();
			//主要是注意回溯栈时,判断栈顶节点是弹出栈打印还是继续向左右方向搜索
			
			
			//后序遍历时,一节点必和其左右子节点相邻且在其后
			//注意不可以使用current.rightchild!=null&¤t.rightchild!=pre来代替判断条件
			if(current.rightchild!=null&¤t.leftchild!=pre&¤t.rightchild!=pre){
				
						s.push(current.rightchild);
			}
			if(current.leftchild!=null&¤t.leftchild!=pre&¤t.rightchild!=pre){
			
					s.push(current.leftchild);
			}
			
			if(current.leftchild==null&¤t.rightchild==null){
				System.out.print(s.pop().data+" ");
				pre=current;
			}
			if(current.leftchild==pre||current.rightchild==pre){
				System.out.print(s.pop().data+" ");
				pre=current;
			}
		}
		System.out.println();
			
	}



方法四:
 下面还有一种方法,使用双栈法,相当巧妙,我很喜欢,注意这一次栈s1是先将左孩子入栈,再将右孩子入栈的。 
  
<span style="white-space:pre">	</span>public void postOrder4(){
		if(root==null) return;
		Stack<Node> s1=new Stack<Node>();
		Stack<Node> s2=new Stack<Node>();
		s1.push(root);
		Node cur=root;
		while(!s1.isEmpty()){
			cur=s1.pop();
			s2.push(cur);
			if(cur.leftchild!=null)
				s1.push(cur.leftchild);
			if(cur.rightchild!=null)
				s1.push(cur.rightchild);
		}
		while(!s2.isEmpty()){
			System.out.print(s2.pop().data+" ");
		}
		System.out.println();
	}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值