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

    xiaoxiao2021-03-25  49

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

    二叉树结构如图:

    /*二叉树结构: 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

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

    转载请注明原文地址: https://ju.6miu.com/read-38375.html

    最新回复(0)