二叉树的深度遍历 递归 和 堆栈法

来源:互联网 发布:陈巧生香炉官网淘宝网 编辑:程序博客网 时间:2024/06/03 19:38
    今天令我欣慰的是我终于弄懂了一个难点:

    用堆栈实现二叉树的后序遍历!! ^_^

    技术在于积累,积累在于坚持,每天进步一点点!

    基于此,把二叉树的遍历总结一下,这里二叉树的层序遍历先不考虑,

    主要是总结关于二叉树的前序、中序和后序遍历,提供了两种思路,递归和堆栈,
    
    堆栈思路中用到了树的高度,因此要会求树的高度。


    1、理解二叉树的前序、中序、后序的概念:

        假设有一颗二叉树如下图所示:

                    A
                  /      \
                 B     C
                / \    /      
               D   E  F
               
        前序:  ABDECF

                首先,访问根节点
                然后,访问左子树
                最后,访问右子树

        中序:  DBEAFC

                首先,访问左子树
                然后,访问根节点
                最后,访问右子树

        后序:  DEBFCA

                首先,访问左子树
                然后,访问右子树
                最后,访问根节点


    2、二叉树的节点

            public class BTNode
            {
                public int value;            // 关键值 根节点
                BTNode left;                // 左子树
                BTNode right;                // 右子树
            }

    
    3、二叉树的递归遍历

        前序遍历:

            public void printPreOrder(BTNode root)
            {
                if (root != null)
                {
                    System.out.print(root.value + " ");
                    printPreOrder(root.left);
                    printPreOrder(root.right);
                }
            }

        中序遍历:

            public void printInOrder(BTNode root)
            {
                if (root != null)
                {
                    printInOrder(root.left);
                    System.out.print(root.value + " ");
                    printInOrder(root.right);
                }
            }

        后序遍历:

            public void printPostOrder(BTNode root)
            {
                if (root != null)
                {
                    printPostOrder(root.left);
                    printPostOrder(root.right);
                    System.out.print(root.value + " ");
                }
            }

    4、二叉树的高度 (递归思想)

            public int height(BTNode root)
            {
                int leftHeight = 0,rightHeight = 0;

                if (root.left != null)
                    leftHeight = height(root.left);
                if (root.right != null)
                    rightHeight = height(root.right);

                return (leftHeight > rightHeight ? leftHeight : rightHeight) + 1;
            }


    5、二叉树的堆栈遍历

        前序遍历:

            public void printInOrder2(BTNode root)
            {
                if (root == null)
                    return;

                ArrayStack<BTNode> stack = new ArrayStack<>(height(root) + 1); // 其实不用加1也行
                BTNode current = root,node = null;

                while (current != null || !stack.isEmpty())
                {
                    while (current != null)                        // 三序遍历永远都是将连续的左子树压栈!
                    {
                        System.out.print(current.value + " ");  // 前序遍历首先在压栈前打印关键字
                        stack.push(current);
                        current = current.left;
                    }

                    node = stack.pop();                        // 前序、中序可以直接弹出,后序不可以,得先窥探。

                    if (node.right != null)                    // 如果某个节点有右子树,那么就将该右子树压栈。
                        current = node.right;
                }

                System.out.println();                        // 打印换行
            }


        中序遍历:

            public void printPreOrder2(BTNode root)
            {
                if (root == null)
                    return;

                ArrayStack<BTNode> stack = new ArrayStack<>(height(root) + 1);
                BTNode current = root,node = null;

                while (current != null || !stack.isEmpty())    // 最终的退出条件为 current == null 和 栈为空
                {
                    while (current != null)                    // 三序遍历永远都是将连续的左子树压栈!
                    {
                        stack.push(current);
                        current = current.left;
                    }

                    node = stack.pop();                        // 弹出,打印
                    System.out.print(node.value + " ");        // 前序、中序只是打印函数的位置不同,其他都相同,后序的相对复杂
                    
                    if (node.right != null)                    // 如果某个节点的有右子树,就将该右子树压栈。
                        current = node.right;
                }

                System.out.println();                        // 打印换行
            }


        后序遍历(较复杂):

            public void printPostOrder2(BTNode root)
            {
                if (root == null)
                    return;

                int h = height(root);            // 同上,先求出树的高度
                ArrayStack<BTNode> stack = new ArrayStack<>(h + 1);  // 其实 h 个空间就够,自己实现的 stack
                BTNode current = root,father = null,node = null;

                while (current != null || !stack.isEmpty())
                {
                    while (current != null)
                    {
                        stack.push(current);
                        current = current.left;
                    }

                    father = stack.peer();            // 先别出栈,先窥探父节点有没有右子树

                    if (father.right != null && father.right != node)   // 括号中的 father.right != node 是关键的条件,
                        current = father.right;                            // 没有这个条件,会造成程序进入死循环!
                    else
                    {
                        node = stack.pop();
                        System.out.print(node.value + " ");
                    }
                }
                System.out.println();
            }


            用堆栈进行二叉树的遍历,前序遍历和中序遍历特别特别相似,只有一句话的位置不同,剩下的都相同,区别二者的不同谨记:

                前序遍历: 中->左->右,应该在节点压入栈之前就要打印出来!
                中序遍历: 左->中->右,应该在节点被栈弹出后再打印出来!

            最不好实现的要数后序遍历了,因为后序遍历遵循 左->右->中 的思路,因此根节点最后输出,在输出打印完左子树的节点
            后,如果有右子树,那么就要首先把右子树再压入栈中,而不是急着把根节点弹出来打印,这是其一。

            其二,后序遍历中的辅助变量中比前序和中序多了一个 father,这是很有意义的,因为在右子树中的节点被栈弹出后,此时
            窥探到栈中右是刚刚判断的是否有右子树的父节点,此时 如果不加以下限定条件,则就又把右子树的节点压入栈中,从而
            造成程序进入死循环状态,该限定条件为:

                    father.right != node;

                即栈中栈顶的节点和刚刚弹出的节点存在着父子关系,确切说是右孩子的关系时,那么程序就不会再次进入 第二个 if
                语句,也就不会再次压右子树入栈,造成死循环了!

            、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、

            最重要的一点就是 堆栈遍历拥有比递归遍历一大优点就是:

                    堆栈遍历的效率要高于递归遍历的效率,原因就是递归造成的重复性太多 !
                    
                    因此,二叉树的深度遍历最好选择 堆栈法遍历!!

                                                                

                                                                明日新晨
                                                               2017.7.20.
阅读全文
0 0