二叉树的非递归遍历
2015-10-09 11:21
483 查看
二叉树是一种经典的数据结构,各大公司面试的时候也喜欢考,我将我所总结积累的二叉树的几种非递归遍历分享一下,欢迎大家提出更多更好的方法:
方法二,使用栈先进后出的特性,简洁,我喜欢。先将中间节点出栈打印,然后依次将右孩子、左孩子入栈,循环该步骤。
两种方法,一种是在向左搜索和向右搜索时,遇到null也入栈,可以保证即使右孩子为null,栈也非空;另一种是向右搜索时做判断,右孩子为空时向上回溯,非空时将右孩子入栈,这就是下面的这种处理方式:
方法一:可以使用flag标记,第一次由中间节点到达右孩子时,flag=1,第二次由右孩子返回中间节点时,判断flag==1即可知右子树已经遍历完了,将中间节点出栈打印
方法二:记录下前一个节点pre,经过中间节点时判断,当右孩子为空或者cur.rightchild==pre即pre等于右孩子,则将中间节点出栈打印,否则向右搜索右子树
方法三:使用栈先进后出的特性,中间节点peek后先右孩子入栈再左孩子入栈。这里要做处理,记录下前一个节点,当current.leftchild==pre||current.rightchild==pre时,表示中间节点的左右子树已经遍历过了,将中间节点打印出栈,否则就要考虑将右左孩子入栈。注意:该方法开始时并没有一下子向左遍历到左下节点。
下面是上面三种方法的代码:
方法四:
下面还有一种方法,使用双栈法,相当巧妙,我很喜欢,注意这一次栈s1是先将左孩子入栈,再将右孩子入栈的。
一、先序遍历
方法一,思想是从根节点一直向左边走,边走边打印,左边走不通了,出栈向右边走,边界条件是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(); }
相关文章推荐
- AVL树-自平衡二叉查找树(Java实现)
- 文件遍历排序函数
- Lua 学习笔记之C API 遍历 Table实现代码
- C#中遍历Hashtable的4种方法
- Erlang中遍历取出某个位置的最大值代码
- C++基于栈实现铁轨问题
- C++实现图的邻接矩阵存储和广度、深度优先遍历实例分析
- C++实现图的邻接表存储和广度优先遍历实例分析
- C语言二叉树的非递归遍历实例分析
- C语言栈的表示与实现实例详解
- 使用C语言构建基本的二叉树数据结构
- C语言实现颠倒栈的方法
- C++非递归队列实现二叉树的广度优先遍历
- php遍历目录方法小结
- 算法系列15天速成 第十天 栈
- 一个目录遍历函数
- php遍历删除整个目录及文件的方法
- PHP遍历文件夹与文件类及处理类用法实例
- PHP遍历XML文档所有节点的方法
- 平衡二叉树