面试题2——遍历二叉树

题目

用先序,中序,后序,分别遍历一个二叉树

思想

1.(递归法)利用递归思想,向左向右遍历一个二叉树

2.(非递归)利用栈和循环,将结点压栈,按不同遍历方式选择不同策略从栈中取出结点输出

PS:后序遍历思想,从根结点开始,将所有最左结点全部压栈,每当一个结点出栈时,都先扫描该结点的右子树,只有当一个结点的左孩子和右孩子结点均被访问过了,才能访问结点自身。(判断当右孩子输出时,则向上回溯)

前序递归遍历算法:访问根结点-->递归遍历根结点的左子树-->递归遍历根结点的右子树

中序递归遍历算法:递归遍历根结点的左子树-->访问根结点-->递归遍历根结点的右子树

后序递归遍历算法:递归遍历根结点的左子树-->递归遍历根结点的右子树-->访问根结点

实现

二叉树结点 BinaryTreeNode.java

//二叉树节点
public class BinaryTreeNode {
    public int data;
    public BinaryTreeNode left;
    public BinaryTreeNode right;

    public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right){
        super();
        this.data = data;
        this.left = left;
        this.right = right;
    }
}

遍历 BinaryTreeTraversing.java

package lms.datastructure.tree;

import javafx.scene.transform.Rotate;

import java.util.Stack;

//遍历二叉树
public class BinaryTreeTraversing {
    /**
                 1
                /  \
                2    3
               / \  / \
              4  5  6  7
              /  \
             8    9
                   \
                   10
     */

    //====先序遍历=========
    //递归 先序
    public static void preOrderRecur(BinaryTreeNode root) {
        if (null != root) {
            System.out.print(root.data + " ");
            preOrderRecur(root.left);
            preOrderRecur(root.right);
        }
    }

    //非递归 先序
    public static void preOrder(BinaryTreeNode root) {
        Stack<BinaryTreeNode> stack = new Stack<BinaryTreeNode>();
        while (true) {
            while (root != null) {
                System.out.print(root.data + " ");
                stack.push(root);
                root = root.left;
            }
            if (stack.isEmpty()) break;
            root = stack.pop();
            root = root.right;
        }
    }

    //=====中序遍历============
    //递归 中序
    public static void inOrderRecur(BinaryTreeNode root) {
        if (null != root) {
            inOrderRecur(root.left);
            System.out.print(root.data + " ");
            inOrderRecur(root.right);
        }
    }

    //中序遍历 非递归
    public static void inOrder(BinaryTreeNode root) {
        Stack<BinaryTreeNode> stack = new Stack<>();
        while (true) {
            //循环,遍历左边
            while (root != null) {
                stack.push(root);
                root = root.left;
            }
            if (stack.isEmpty()) break;
            //无右叶子的时候,从栈中弹出
            root = stack.pop();
            System.out.print(root.data + " ");
            //无右叶子的时候,从栈中弹出
            root = root.right;
        }
    }

    //=====后序遍历============
    //递归 后序
    public static void postOrderRecur(BinaryTreeNode root) {
        if (null != root) {
            postOrderRecur(root.left);
            postOrderRecur(root.right);
            System.out.print(root.data + " ");
        }
    }

    //非递归 后序(核心思想:每次左子树遍历完之后,每一步判断是否右子树为null,不为null指针移动到右子树上继续进行 “左->右->根” 步骤)
    public static void postOrder(BinaryTreeNode root) {
        Stack<BinaryTreeNode> stack = new Stack<>();
        while (true) {
            if (null != root) {
                stack.push(root);
                root = root.left;
            } else {
                if (stack.isEmpty()) return;
                //到叶子节点 判断如果是叶子节点(是否为右孩子结点),则输出
                if (null == stack.lastElement().right) {
                    root = stack.pop();
                    System.out.print(root.data + " ");
                    //右子树右叶子访问之后向上回溯
                    while (root == stack.lastElement().right) {
                        System.out.print(stack.lastElement().data + " ");
                        root = stack.pop();
                        //System.out.print(root.data + " ");
                        if (stack.isEmpty()) break;
                    }
                }

                //移动指针到节点的右子树上
                if (!stack.isEmpty()) {
                    root = stack.lastElement().right;
                } else {
                    //终止条件
                    root = null;
                }
            }
        }
    }

    public static void main(String[] args) {
        /**
                                 1
                                /  \
                               2    3
                              / \  / \
                             4  5  6  7
                               /  \
                              8    9
                                    \
                                    10
         */
        BinaryTreeNode node10 = new BinaryTreeNode(10, null, null);
        BinaryTreeNode node8 = new BinaryTreeNode(8, null, null);
        BinaryTreeNode node9 = new BinaryTreeNode(9, null, node10);
        BinaryTreeNode node4 = new BinaryTreeNode(4, null, null);
        BinaryTreeNode node5 = new BinaryTreeNode(5, node8, node9);
        BinaryTreeNode node6 = new BinaryTreeNode(6, null, null);
        BinaryTreeNode node7 = new BinaryTreeNode(7, null, null);
        BinaryTreeNode node2 = new BinaryTreeNode(2, node4, node5);
        BinaryTreeNode node3 = new BinaryTreeNode(3, node6, node7);
        BinaryTreeNode node1 = new BinaryTreeNode(1, node2, node3);

        System.out.println("====先序遍历====");
        preOrderRecur(node1);
        System.out.println("");
        System.out.println("====先序遍历非递归====");
        preOrder(node1);
        System.out.println("");
        System.out.println("====中序遍历====");
        inOrderRecur(node1);
        System.out.println("");
        System.out.println("====中序遍历非递归====");
        inOrder(node1);
        System.out.println("");
        System.out.println("====后序遍历====");
        postOrderRecur(node1);
        System.out.println("");
        System.out.println("====后序遍历非递归====");
        postOrder(node1);
        System.out.println("");
    }

}

 后序遍历——双栈法

后序遍历的顺序:左孩子 -> 右孩子 -> 根结点。打印每个结点需要访问根结点三次,先从根结点开始找到左孩子,返回再找到右孩子,再返回到根结点,需要访问根结点三次,直接按照当前顺序进行遍历不知如何下手,那么我们可以换一个角度去考虑。不妨借助一个栈先存储 根结点 -> 右孩子 -> 左孩子 的结果,再将其依次弹出就是后序遍历的顺序了。

将前序遍历的逻辑改写为:弹出每个栈顶结点作为当前结点并存储到一个辅助栈中,如果当前结点有左孩子就先压入左孩子,如果有右孩子就后压入右孩子,每次取栈顶弹出到辅助栈中。最后将得到的辅助栈中元素依次弹出得到的就是后序遍历的结果。

    //后序遍历 非递归 双栈法
    public static void postOrderTwoStack(BinaryTreeNode root) {
        Stack<BinaryTreeNode> stack1 = new Stack<>();    // 辅助栈,存储 根 -> 右 -> 左 的结果
        Stack<BinaryTreeNode> stack2 = new Stack<>();
        if (root != null) {
            stack1.push(root);
            while (!stack1.isEmpty()) {
                root = stack1.pop();
                stack2.push(root);
                if (root.left != null) {
                    stack1.push(root.left);            // 有左孩子就先压入左孩子
                }
                if (root.right != null) {
                    stack1.push(root.right);           // 有右孩子就后压入右孩子
                }
            }
            while (!stack2.isEmpty()) {
                System.out.print(stack2.pop().data + " ");     // 逆序打印 根 -> 右 -> 左 的结果,就是后序遍历的结果
            }
        }
    }

 

输出结果

====先序遍历====
1 2 4 5 8 9 10 3 6 7
====先序遍历非递归====
1 2 4 5 8 9 10 3 6 7
====中序遍历====
4 2 8 5 9 10 1 6 3 7
====中序遍历非递归====
4 2 8 5 9 10 1 6 3 7
====后序遍历====
4 8 10 9 5 2 6 7 3 1
====后序遍历非递归====
4 8 10 9 5 2 6 7 3 1

 

posted @ 2019-02-19 16:14  江东邮差  阅读(169)  评论(0编辑  收藏  举报