您的位置:首页 > 其它

二叉树的非递归遍历

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

一、先序遍历

方法一,思想是从根节点一直向左边走,边走边打印,左边走不通了,出栈向右边走,边界条件是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();
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息