二叉树输出全部路径问题的非递归式解决方案

问题:对于一棵二叉树,输出所有从根结点到叶子结点的路径。

主要思路是和二叉树的非递归式后序遍历差不多,都是先用一个栈来存储已经访问过的结点,直至遍历完它自己、它的左子树、它的右子树。但是后序遍历和其它两种遍历方式还是有些不同,前序遍历的过程是:结点入栈-->访问结点-->访问左子树-->返回结点-->结点出栈-->访问右子树;中序遍历的过程是:结点入栈-->访问左子树-->返回结点-->访问结点-->结点出栈-->访问右子树;而后序遍历的过程是:结点入栈-->访问左子树-->返回结点-->访问右子树-->返回结点-->访问结点-->结点出栈。可以看到,后序遍历有再次返回了结点,这就带来一个问题,怎么判断某次返回结点到底是从左子树返回的?还是从右子树返回的?

解决后序遍历的这个问题的方法是:当从栈中弹出一个元素时,检查这个元素与栈顶元素的右子结点是否相同,如果相同说明是从右子树返回,此时已经完成了左右子树的遍历,下一步只需再弹出元素并访问该结点即可。

说了半天后序遍历,其实在输出路径的问题上我们采用的是前序遍历方式,先访问一个结点,然后是左子树,然后才是右子树,毕竟这样才比较自然一点。只是在写代码的过程中遇到了一个问题,也需要判断栈中的某个结点是否已经访问过它的左右子树,此时采用了和判断后序遍历是从哪个子树返回一样的解决方案。

代码如下:

import java.util.Stack;

public class PathFromRootToLeaf {
    static class Node<T> {
        T t;
        Node left;
        Node right;

        @Override
        public String toString() {
            return t.toString();
        }
    }

    public static void printPathStack(Stack stack) {
        for (int i = 0; i < stack.size(); i++) {
            System.out.print(stack.elementAt(i) + " ");
        }
        System.out.println();
    }

    //可以看到和前序遍历的过程差不多
    public static void printPath(Node node) {
        if (node == null) return;
        Stack<Node> path = new Stack<>();
        end:
        while (true) {
            //将左结点入栈,直至没有左子树
            while (node != null) {
                path.push(node);
                node = node.left;
            }

            if (path.isEmpty()) break;

            //访问栈顶的结点,如果是叶子结点则输出路径,否则访问其右结点
            node = path.peek();

            //如果是叶子结点
            if (isLeaf(node)) {
                printPathStack(path);
                path.pop();  //弹出node
                Node parent = path.peek();  //由于栈中存储的是路径,所以path.peek()一定是node的父结点
                //如果node是parent的右子树,说明parent左子树已经遍历过,那么需要继续弹出parent结点。
                //如果parent没有右子树,同样地需要弹出parent结点,因为parent的所有子树已经遍历过。
                //这个过程需要重复进行,找出路径中一个结点,它的右子树需要遍历。
                while (parent.right == node || parent.right == null) {
                    node = path.pop();
                    //如果栈为空,说明根结点的右子树也已经遍历完成,那么直接退出循环
                    if (!path.isEmpty()) {
                        parent = path.peek();
                    } else {
                        break end;
                    }
                }
                //找到一个parent的右子树没有遍历过
                node = parent;
            }
            //访问node的右子树
            node = node.right;
        }
    }

    private static boolean isLeaf(Node node) {
        return node.right == null && node.left == null;
    }

    public static void preOrder(Node root) {
        if (root == null) return;
        Stack<Node> stack = new Stack<>();
        while (true) {
            while (root != null) {
                System.out.println(root);
                stack.push(root);
                root = root.left;
            }
            if (stack.isEmpty()) break;
            root = stack.pop();
            root = root.right;
        }
    }

    public static void main(String[] args) {
        Node<Integer> root = new Node<>();
        Node<Integer> a = new Node<>();
        Node<Integer> b = new Node<>();
        Node<Integer> c = new Node<>();
        Node<Integer> d = new Node<>();
        Node<Integer> e = new Node<>();
        Node<Integer> f = new Node<>();
        Node<Integer> g = new Node<>();
        Node<Integer> h = new Node<>();
        root.t = 1;
        a.t = 2;
        b.t = 3;
        c.t = 4;
        d.t = 5;
        e.t = 6;
        f.t = 7;
        g.t = 8;
        h.t = 9;
        root.left = a;
        root.right = b;
        a.left = c;
        a.right = d;
        c.right = e;
        e.left = f;
        e.right = g;
        d.right = h;
        printPath(root);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值