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

来源:互联网 发布:网络信息有限公司名字 编辑:程序博客网 时间:2024/06/18 13:55

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

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

二叉树的结构

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).

0 0