二叉树的非递归遍历---JAVA实现

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:57   1695   0

二叉树的递归遍历方式是很简单的,当需要用非递归的方式遍历时,就需要借助栈这种数据结构,以前序遍历为例,其定义为先访问根节点,再以前序方式访问左子树,再以前序遍历方式访问右子树,这是个递归的定义。对于前序遍历,最先访问的是根,然后是根左边的孩子,在继续访问孩子的左边孩子,走到底部,再从底部开始倒退回来,逐渐访问右边孩子,很自然的想到使用栈这种数据结构......(可以画一个二叉树看看其前序遍历的过程就明白了),下面以代码说明:

二叉树结构如图:

/*二叉树结构:
       A
      /  \
     B    C
    / \   /
   E   F  I
      / \  \
     G   H  J
*/

前序、中序、后序的递归、非递归访问:

代码还包括递归创建二叉树的部分,其中,后序遍历有两种非递归实现方法(对应使用了一个栈或两个栈)

public class BinTreeTraverse {
 /**
  * @ 二叉树的非递归遍历
  */
 public static void main(String[] args) {

  String str="ABE##FG##H##CI#J###";
  TreeNode<Character> root=createBinTree(new StringBuilder(str));
  
  System.out.print("********************preOrder******************\n");
  preOrder(root);
  System.out.println();
  preOrderLoop(root);
  
  System.out.print("\n********************inOrder******************\n");
  inOrder(root);
  System.out.println();
  inOrderLoop(root);
    
  System.out.print("\n*******************postOrder*******************\n");
  postOrder(root);
  System.out.print("\n双栈方式:");
  postOrderLoop(root);
  System.out.print("\n单栈方式:");
  postOrderLoop2(root);
 }
 
 //***************************前序********************************
 public static void preOrder(TreeNode<Character> root){
  if(root!=null){
   System.out.print(root.data+" ");
   preOrder(root.leftChildren); 
   preOrder(root.rightChildren);
  }
 }
 //PreOrder的非递归实现——需要借助栈空间实现
 public static void preOrderLoop(TreeNode<Character> root){
  //根据递归的过程来想,它每遇到一个节点,就以之为根节点,不断向左下放遍历,入栈,直到空
  //然后栈弹出,又以弹出点右孩子为根(非空的),继续刚才左下滑的过程
  MyStack<TreeNode<Character>> stack=new MyStack<TreeNode<Character>>();
  TreeNode<Character> temp=root;
  while(temp!=null || !stack.isEmpty()){
   while(temp!=null){
    visit(temp);
    stack.push(temp);
    temp=temp.leftChildren;
   }
   if(!stack.isEmpty()){
    temp=stack.pop();
    temp=temp.rightChildren;
   }
  }
 }
 //*****************************中序**********************************
 public static void inOrder(TreeNode<Character> root){
  if(root!=null){
   inOrder(root.leftChildren);
   visit(root);
   inOrder(root.rightChildren);
  }
 }
 public static void inOrderLoop(TreeNode<Character> root){
  TreeNode<Character> temp=root;
  MyStack<TreeNode<Character>> stack =new MyStack<TreeNode<Character>>();
  while(temp!=null || !stack.isEmpty()){
   while(temp!=null){
    stack.push(temp);
    temp=temp.leftChildren;
   }
   
   if(!stack.isEmpty()){
    temp=stack.pop();
    visit(temp);
    temp=temp.rightChildren;
   }
  }
 }
 
 //********************************后序**********************************
 public static void postOrder(TreeNode<Character> root){
  if(root!=null){
   postOrder(root.leftChildren);
   postOrder(root.rightChildren);
   visit(root);
  }
 }
 public static void postOrderLoop(TreeNode<Character> root){
  /*(可结合后序遍历结果进行分析)
   * 与前序遍历相反,后序遍历相当于每次从根节点开始,一直向右下方向搜寻直到空,逐个入栈,遇到null后,
   * 依次出栈,让出栈点的左孩子执行相同的操作(需要一个辅助栈保存);最后将栈的元素一起弹出
   * 因此:后序遍历需要用两个栈
   * */
  TreeNode<Character> temp=root;
  MyStack<TreeNode<Character>> stack=new MyStack<TreeNode<Character>>();
  MyStack<TreeNode<Character>> stackHelp=new MyStack<TreeNode<Character>>();
  while(temp!=null || !stack.isEmpty()){
   while(temp!=null){
    stack.push(temp);
    stackHelp.push(temp);//此句是唯一与前序遍历不同的地方(进入辅助栈)
    temp=temp.rightChildren;
   }
   if(!stack.isEmpty()){
    temp=stack.pop();
    temp=temp.leftChildren;
   }
  }
  while(!stackHelp.isEmpty()){
   temp=stackHelp.pop();
   visit(temp);
  }
 }
 
 /*使用单个栈也可实现非递归的后序遍历
  * 需要有一个节点记录上次访问过的节点
  * */
 public static void postOrderLoop2(TreeNode<Character> root){
  TreeNode<Character> temp = root;
  TreeNode<Character> preNode =null;
  MyStack<TreeNode<Character>> stack =new MyStack<TreeNode<Character>>();
  while(temp!=null || !stack.isEmpty()){
   //同前序一样,左边所有节点入栈
   while(temp!=null){
    stack.push(temp);
    temp=temp.leftChildren;
   }
   if(!stack.isEmpty()){
    temp=stack.peek();
    if(temp.rightChildren!=null && temp.rightChildren!=preNode){
     temp=temp.rightChildren;
    }else{//说明右子树为空或已经访问过
     temp=stack.pop();
     visit(temp);
     preNode=temp;
     temp=null;   //防止刚出栈的节点的左孩子继续入栈(即跳过上面的while循环)
    }
   }
   
  }
  
 }
 
 //********************************************************************
 
 private static void  visit(TreeNode<Character> root){
  System.out.print(root.data+" ");
 }
 
 //递归的创建二叉树
 public static TreeNode<Character> createBinTree(StringBuilder sb){
  //只能传StringBuilder,不能传String,因为第二次递归必须使用第一次递归使用完之后的数据
  //string虽然也传递的是字串的引用,但递归过程中并不能改变其值substring()返回的是新字串
  char data=sb.charAt(0);
  sb.deleteCharAt(0);
  TreeNode<Character> rootNode=null;
  if(data!='#'){
   rootNode=new TreeNode<Character>(data);
   rootNode.leftChildren=createBinTree(sb);
   rootNode.rightChildren=createBinTree(sb);
  }
  return rootNode;
 }
}

class TreeNode<E>{
 E data;
 TreeNode<E> leftChildren;
 TreeNode<E> rightChildren;
 TreeNode(E data){
  this.data=data;
  leftChildren=null;
  rightChildren=null;
 }
}
运行结果:

********************preOrder******************
A B E F G H C I J 
A B E F G H C I J 
********************inOrder******************
E B G F H A I J C 
E B G F H A I J C 
*******************postOrder*******************
E G H F B J I C A 
双栈方式:E G H F B J I C A 
单栈方式:E G H F B J I C A 

可以看出,非递归的方式均使用了栈这种数据结构,这也是栈在程序设计中最常见的用途之一。

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP