zoukankan      html  css  js  c++  java
  • 面试题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

  • 相关阅读:
    初学oracle,创建数据库时出现ora00988:缺少或无效口令 以及登陆
    NUnit单元测试笔记
    请给Array本地对象增加一个原型方法,它的用途是删除数组条目中重复的条目(可能有多个),返回值是一个仅包含被删除的重复条目的新数组。
    给iframe添加onload事件
    给超链接去焦点框(虚线框)时遇到的问题
    FCKeditor上传文件重命名for php
    PDF Xchange Pro 3.6的注册码
    APNG
    一个as3的alert
    jQuery遮罩弹窗
  • 原文地址:https://www.cnblogs.com/limaosheng/p/10401735.html
Copyright © 2011-2022 走看看