zoukankan      html  css  js  c++  java
  • 二叉树的非递归遍历

     二、三种遍历方式的非递归实现

        为了便于理解,这里以下图的二叉树为例,分析二叉树的三种遍历方式的实现过程。


              1、前序遍历的非递归实现 

    根据先序遍历的顺序,先访问根节点,再访问左子树,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的先序遍历顺序为:ABDECF。非递归的实现思路如下:

    对于任一节点P

    1)输出节点P,然后将其入栈,再看P的左孩子是否为空;

    2)P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;

    3)P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;

    4)若不为空,则循环至1)操作;

    5)如果为空,则继续出栈,但不输出,同时将出栈节点的右孩子置为当前节点,看其是否为空,重复4)和5)操作;

    6)直到当前节点PNULL并且栈空,遍历结束。

    下面以上图为例详细分析其先序遍历的非递归实现过程:

     

    public class Solution {
        public ArrayList<Integer> preorderTraversal(TreeNode root) {
            ArrayList<Integer> returnList = new ArrayList<Integer>();
            if(root == null)
                return returnList;
            Stack<TreeNode> stack = new Stack<TreeNode>();
            stack.push(root);
            while(!stack.empty()){
                TreeNode n = stack.pop();
                returnList.add(n.val);
                if(n.right != null){
                    stack.push(n.right);
                }
                if(n.left != null){
                    stack.push(n.left);
                }
            }
            return returnList;
        }
    
        public java.util.ArrayList<Integer> preorderTraversal(TreeNode root) {
            java.util.Stack<TreeNode> stack=new java.util.Stack<TreeNode>();
            if(root==null)
                return null;
            java.util.ArrayList<Integer> list=new java.util.ArrayList<Integer>();
            TreeNode head=root;
            while(stack.size()!=0||head!=null){
                while(head!=null){
                    System.out.println(head.val);
                    list.add(head.val);
                    stack.push(head);
                    head=head.left;
                }
                head=stack.pop();
                if(head.right!=null){
                    head=head.right;
                }else{
                    if(stack.size()>0){
                        head=stack.pop();
                        head=head.right;
                    }else{
                        break;
                    }
                    
                }
            }
            return list;
        }
    
    }

    首先,从根节点A开始,根据操作1),输出A,并将其入栈,由于A的左孩子不为空,根据操作2),将B置为当前节点,再根据操作1),将B输出,并将其入栈,由于B的左孩子也不为空,根据操作2),将D置为当前节点,再根据操作1),输出D,并将其入栈,此时输出序列为ABD

    由于D的左孩子为空,根据操作3),将栈顶节点D出栈,但不输出,并将其右孩子置为当前节点;

    由于D的右孩子为空,根据操作5),继续将栈顶节点B出栈,但不输出,并将其右孩子置为当前节点;

    由于B的右孩子E不为空,根据操作1),输出E,并将其入栈,此时输出序列为:ABDE

    由于E的左孩子为空,根据操作3),将栈顶节点E出栈,但不输出,并将其右孩子置为当前节点;

    由于E的右孩子为空,根据操作5),继续将栈顶节点A出栈,但不输出,并将其右孩子置为当前节点;

    由于A的右孩子C不为空,根据操作1),输出C,并将其入栈,此时输出序列为:ABDEC

    由于A的左孩子F不为空,根据操作2),则将F置为当前节点,再根据操作1),输出F,并将其入栈,此时输出序列为:ABDECF

    由于F的左孩子为空,根据操作3),将栈顶节点F出栈,但不输出,并将其右孩子置为当前节点;

    由于F的右孩子为空,根据操作5),继续将栈顶元素C出栈,但不输出,并将其右孩子置为当前节点;

    此时栈空,且C的右孩子为NULL,因此遍历结束。

    2、中序遍历的非递归实现

     

    根据中序遍历的顺序,先访问左子树,再访问根节点,后访问右子树,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的中序遍历顺序为:DBEAFC。非递归的实现思路如下:

    对于任一节点P

    1)P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;

    2)P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;

    3)若不为空,则重复1)和2)的操作;

    4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;

    5)直到当前节点PNULL并且栈为空,则遍历结束。

     

    下面以上图为例详细分析其中序遍历的非递归实现过程:

    首先,从根节点A开始,A的左孩子不为空,根据操作1)将A入栈,接着将B置为当前节点,B的左孩子也不为空,根据操作1),将B也入栈,接着将D置为当前节点,由于D的左子树为空,根据操作2),输出D

    由于D的右孩子也为空,根据操作4),执行出栈操作,将栈顶结点B出栈,并将B置为当前节点,此时输出序列为DB

    由于B的右孩子不为空,根据操作3),将其右孩子E置为当前节点,由于E的左孩子为空,根据操作1),输出E,此时输出序列为DBE

    由于E的右孩子为空,根据操作4),执行出栈操作,将栈顶节点A出栈,并将节点A置为当前节点,此时输出序列为DBEA

    此时栈为空,但当前节点A的右孩子并不为NULL,继续执行,由于A的右孩子不为空,根据操作3),将其右孩子C置为当前节点,由于C的左孩子不为空,根据操作1),将C入栈,将其左孩子F置为当前节点,由于F的左孩子为空,根据操作2),输出F,此时输出序列为:DBEAF

    由于F的右孩子也为空,根据操作4),执行出栈操作,将栈顶元素C出栈,并将其置为当前节点,此时的输出序列为:DBEAFC

    由于C的右孩子为NULL,且此时栈空,根据操作5),遍历结束。

    3、后序遍历的非递归实现

     

     

    根据后序遍历的顺序,先访问左子树,再访问右子树,后访问根节点,而对于每个子树来说,又按照同样的访问顺序进行遍历,上图的后序遍历顺序为:DEBFCA。后序遍历的非递归的实现相对来说要难一些,要保证根节点在左子树和右子树被访问后才能访问,思路如下:

     

    对于任一节点P

    1)先将节点P入栈;

    2)P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;

    3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);

    4)直到栈空,遍历结束。

     

       下面以上图为例详细分析其后序遍历的非递归实现过程:

    首先,设置两个指针:Cur指针指向当前访问的节点,它一直指向栈顶节点,每次出栈一个节点后,将其重新置为栈顶结点,Pre节点指向上一个访问的节点;

    Cur首先指向根节点APre先设为NULL,由于A存在左孩子和右孩子,根据操作3),先将右孩子C入栈,再将左孩子B入栈,Cur改为指向栈顶结点B

    由于B的也有左孩子和右孩子,根据操作3),将ED依次入栈,Cur改为指向栈顶结点D

    由于D没有左孩子,也没有右孩子,根据操作2),直接输出D,并将其出栈,将Pre指向DCur指向栈顶结点E,此时输出序列为:D

    由于E也没有左右孩子,根据操作2),输出E,并将其出栈,将Pre指向ECur指向栈顶结点B,此时输出序列为:DE

    由于B的左右孩子已经被输出,即满足条件Pre==Cur->lchildPre==Cur->rchild,根据操作2),输出B,并将其出栈,将Pre指向BCur指向栈顶结点C,此时输出序列为:DEB

    由于C有左孩子,根据操作3),将其入栈,Cur指向栈顶节点F

    由于F没有左右孩子,根据操作2),输出F,并将其出栈,将Pre指向FCur指向栈顶结点C,此时输出序列为:DEBF

    由于C的左孩子已经被输出,即满足Pre==Cur->lchild,根据操作2),输出C,并将其出栈,将Pre指向CCur指向栈顶结点A,此时输出序列为:DEBFC

    由于A的左右孩子已经被输出,根据操作2),输出A,并将其出栈,此时输出序列为:DEBFCA

    此时栈空,遍历结束。

  • 相关阅读:
    JAVA与.NET的相互调用——通过Web服务实现相互调用
    WCF大数据量传输配置
    WCF大数据量传输解决方案
    C# #if DEBUG
    我对CSS vertical-align的一些理解与认识(一)
    htmlparser使用例子(全) 转载
    【HtmlParser】HtmlParser使用
    HTMLParser 使用详解
    htmlparser源码简单分析
    利用OpenXml生成Word2007文档
  • 原文地址:https://www.cnblogs.com/csxf/p/3638571.html
Copyright © 2011-2022 走看看