二叉树的递归和非递归前、中、后序遍历

1、二叉树树的递归遍历,程序简洁,前、中、后遍历相差不大。

本例中用到的二叉树为:
这里写图片描述
前序遍历为:10 6 4 8 14 12 16
中序遍历为:4 6 8 10 12 14 16
后续遍历为:4 8 6 12 16 14 10
非递归代码:`

递归版的前序遍历:

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) {
        if(root==null)
            return ;    
        list.add(root.val); 
        if(root.leftchild!=null)
            TraverseTree(root.leftchild,list);
        if(root.rightchild!=null)
            TraverseTree(root.rightchild,list);
    }

递归版的中序遍历:

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) {
        if(root==null)
            return ;    
        if(root.leftchild!=null)
            TraverseTree(root.leftchild,list);
        list.add(root.val); 
        if(root.rightchild!=null)
            TraverseTree(root.rightchild,list);
    }

递归版的后序遍历

private static void TraverseTree(BinaryTreeNode root,ArrayList<Integer> list) {
        if(root==null)
            return ;    
        if(root.leftchild!=null)
            TraverseTree(root.leftchild,list);
        if(root.rightchild!=null)
            TraverseTree(root.rightchild,list);
        list.add(root.val); 
    }

从上面的代码块可以看出,递归版的前、中、后序遍历唯一的差别处就是什么时候将根节点保存到list中;

2、非递归版的二叉树遍历
很多的递归问题都可以用循环来解决,事实上递归也是基于栈来实现的,但是利用栈实现递归的时候,就得考虑栈的劣势可能引发的问题。

这儿顺便总结一下递归用栈的缺点:

  1. 递归由于是函数调用自身,而函数调用是有时间和空间消耗的:每一次函数调用都需要在内存栈中分配空间以保存参数、临时变量及返回地址,而且往栈里亚人和弹出数据都需要时间。
  2. 递归中有很多计算都是重复的,从而对性能能带来很大的负面影响。这一点在斐波那契数列的计算上体现尤其的明显。
  3. 除了效率之外,递归还有可能引起更严重的问题:调用栈溢出。每一调用函数都需要在内存栈中为其分配空间,而每个进程的栈容量是有限的,当递归调用的层级太多时,就会超出栈的容量,从而导致调用栈溢出。

虽然栈的递归使用有很多的问题,但是在非递归计算过程中,栈的使用又是不可避免的,特别是在树的循环遍历中需要保存之前访问过的节点,那么这时也只能使用栈来中转啦!

非递归前序遍历代码:

public static void preOrderTraversal(BinaryTreeNode root,ArrayList<Integer> list){
        if(root==null)
            return ;
        Stack<BinaryTreeNode> s=new Stack<BinaryTreeNode>();
        BinaryTreeNode p=root;
        while(p!=null||!s.isEmpty()){
            while(p!=null){
                list.add(p.val);
                s.push(p);
                p=p.leftchild;
            }
            if(!s.isEmpty()){
                p=s.pop();
                p=p.rightchild;
            }
        }
        }

非递归中序遍历代码:

public static void InOrderTraversal(BinaryTreeNode root ,ArrayList<Integer> list){
        Stack<BinaryTreeNode> stack=new Stack<BinaryTreeNode>();
        if(root==null)
            return ;
        BinaryTreeNode p=root;
        while(p!=null||!stack.isEmpty()){
            while(p!=null){
                stack.push(p);
                p=p.leftchild;
            }
            if(!stack.isEmpty()){
                p=stack.pop();
                list.add(p.val);
                p=p.rightchild;
            }
        }
    }

非递归后续遍历代码:

public static void PostOrderTraversal(BinaryTreeNode root,ArrayList<Integer> list){
        if(root==null)
            return ;
        Stack<BinaryTreeNode> stack=new Stack<BinaryTreeNode>();
        Stack<Integer> s=new Stack<Integer>();
        BinaryTreeNode p=root;
        while(p!=null||!stack.isEmpty()){
            while(p!=null){
                stack.push(p);
                s.push(p.val);
                p=p.rightchild;
            }
            if(!stack.isEmpty()){
                p=stack.pop();
                p=p.leftchild;
            }
        }
        while(!s.isEmpty()){
            list.add(s.pop());
        }
    }

后续遍历代码较前、中序遍历代码有较大的改动因为根节点是最后被访问的。但是通过观察后续遍历的结果:4 8 6 12 16 14 10 ,其逆过来看为:10 14 16 12 6 8 4,再看前序遍历的结果:10 6 4 8 14 12 16,二者比较发现,逆后序遍历就是在前序遍历的基础上稍作改动:先访问根节点,再访问右孩子,再访问做孩子,结果就是逆后序遍历,得到逆后序遍历,再将其反转,就可以得到后续遍历的结果。

测试用的代码如下:

class BinaryTreeNode{
    int val;
    BinaryTreeNode leftchild=null;
    BinaryTreeNode rightchild=null;
    public BinaryTreeNode(int val){
        this.val=val;
    }

}
public class TreeTraverse {

    public static void main(String[] args) {
        BinaryTreeNode root=new BinaryTreeNode(10);
        BinaryTreeNode node6=new BinaryTreeNode(6);
        BinaryTreeNode node14=new BinaryTreeNode(14);
        root.leftchild=node6;
        root.rightchild=node14;
        BinaryTreeNode node4=new BinaryTreeNode(4);
        BinaryTreeNode node8=new BinaryTreeNode(8);
        node6.leftchild=node4;
        node6.rightchild=node8;
        BinaryTreeNode node12=new BinaryTreeNode(12);
        BinaryTreeNode node16=new BinaryTreeNode(16);
        node14.leftchild=node12;
        node14.rightchild=node16;
        ArrayList<Integer> list= new ArrayList<Integer>();
        TraverseTree(root,list);
        preOrderTraversal(root,list);
        InOrderTraversal(root,list);
        PostOrderTraversal(root,list);
        System.out.println(list);
    }
}

前、中、后序遍历结果如下:

前序遍历结果为:
[10, 6, 4, 8, 14, 12, 16]
中序遍历结果为:
[4, 6, 8, 10, 12, 14, 16]
后序遍历结果为:
[4, 8, 6, 12, 16, 14, 10]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值