二叉树遍历的非递归实现(java版)

在前面的这篇文章中我写了二叉树遍历的递归实现,在这篇文章中我将讲述下二叉树的非递归实现。

大多数的递归问题的非递归算法,需要用栈来消除递归。栈是一种存储容器,同时又是一种控制结构,栈先进先出的控制结构,同时,调用时可用栈来保留必要的信息,退出时,可以从栈中取出信息,进行后续的处理。所以设计二叉树的遍历非递归算法, 需要用栈来保留节点的信息。

二叉树的结构

class Btree<T>{
    T value;
    Btree<T> left;
    Btree<T> right;

   BNode(T obj, BNode<T> left, BNode<T> right){
        this.value = obj;
        this.left = left;
        this.right = right;
    }
}

public void visitDate(Btree<T> root){
        if (root != null) {
            System.out.println(root.value);
        }
    }

1.先序遍历二叉树的非递归实现
遍历思路:
(1).访问根节点,根节点入栈并进入其左子树,进而访问左子树的根节点并入栈,在进入下一层左子树,……,如此重复,直至当前节点为空。
(2).如栈非空,则从栈顶退出上一层的节点,并进入该节点的右子树。

代码如下:

public void preOder(Btree<T> root){
        Stack<Btree<T>> stack = new Stack<Btree<T>>();
        Btree<T> p = root;//保留当前的节点

        while(p != null || !stack.isEmpty()){
            //遍历这个节点的子节点,直到到达这个节点的叶子节点
            while(p != null){
                visitDate(p);//输出当前节点
                stack.push(p);//把当前节点保存到堆栈中,以便遍历完左子树后遍历右子树
                p = p.left;
            }
        }

        //最后一个叶子节点输出后,在栈中取该节点的根节点,遍历这个根节点的右孩子。
        if (!stack.isEmpty()) {
            Btree<T> pop = stack.pop();
            p = pop.right;
        }
    }

2.中序遍历二叉树的非递归实现
思路:
1.根节点入栈,进入其左子树,进而左子树的根节点入栈,进入下一层左子树,….,如此重复,直至当前节点为空·。
2.若栈非空,则退栈,访问出栈节点,并进入其右子树。
代码:

/*
     * 中根序遍历
     */
    public void midOrder(Btree<T> root){
        Stack<Btree<T>> stack = new Stack<Btree<T>>();
        Btree<T> p = root;//保存当前节点
        while(p != null || !stack.isEmpty()){
            if (p != null) {
                stack.push(p);//当前节点入栈
                p = p.left;//遍历他的左节点
            }else{
                p = stack.pop();//当前节点为空,则取出栈顶元素,
                visitDate(p);//打印栈顶元素
                p = p.right;//遍历该节点的右节点
            }
        }
    }

3.后序遍历二叉树的非递归实现
思路:
1.根节点入栈,进入其左子树,进而左子树的根节点入栈,进入下一层左子树,……,如此重复,直至当前节点为空。
2.若栈非空,如果栈顶节点p的右子树为空,或者p的右孩子是刚访问的节点q,则退栈、访问p节点,并将p置为空,如果栈顶节点p有右子树且右子树为访问,则进入p的右子树。
我的代码:

/*
     * 后续遍历
     */
    public void lastOrder(Btree<T> root){
        Stack<Btree<T>> stack = new Stack<Btree<T>>();
        Btree<T> p = root;//保存当前节点
        Btree<T> q = null;//记录最后一个输出的节点,用于检验是不是根节点的右节点

        while(p != null || !stack.isEmpty()){
            //由根节点向下遍历,知道找到该根节点下的最后一个叶子节点
            while (p != null) {
                stack.push(p);//非叶子节点入栈
                p = p.left;//指向该节点的左孩子
            }

            //p为空,栈非空,说明遍历完了左孩子,处于叶子节点状态
            if (!stack.isEmpty()) {
                p = stack.pop();//栈顶出栈

                //因为如果该节点有右节点,肯定是先访问完右节点才开始访问跟节点的
                //p.right == null:表示没有右节点,可以直接访问根节点
                //p.right == q:刚访问完该节点右节点,则可以访问我该节点
                if (p.right == null || p.right == q) {
                    visitDate(p);//访问当前节点
                    q = p;//记录这个节点
                    p = null;
                }else{//开始遍历右孩子
                    p = p.right;
                }
            }
        }
    }

非递归算法的时间复杂度:每个节点都是出栈一次,入栈一次,每个节点访问一次,对于有n个节点的二叉树,设访问每个节点是轻量级的,则上述二叉树的非递归遍历算法的时间复杂度是O(n).

非递归算法的空间复杂度:对于深度为k的二叉树,上述四个算法所需的栈空间与二叉树的深度k成正比,因此空间复杂度是O(k).

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值