leetcode中的二叉树经典问题(一)

每一个类型的数据结构题目一般都有该数据结构特征所带来的解决秘诀。对于二叉树来说,最大的宝典莫过于递归。下面来看看leetcode二叉树中的经典问题。

二叉树的前中后序遍历(递归方式以及非递归方式)
leetcode144前序遍历 https://leetcode.com/problems/binary-tree-preorder-traversal/
leetcode94 中序遍历 https://leetcode.com/problems/binary-tree-inorder-traversal
leetcode145 后序遍历 https://leetcode.com/problems/binary-tree-postorder-traversal
对于以上的三种遍历方式,用递归方式,很简单,下面贴出前序遍历的递归示例代码:

public static List<Integer> preorderTraversal(TreeNode root) {
//        递归版本
        List<Integer> list = new ArrayList<Integer>();
        if(root == null)return list;
        list.add(root.val);
        list.addAll(preorderTraversal_v1(root.left));
        list.addAll(preorderTraversal_v1(root.right));
        return list;
    }

中序遍历和后续遍历同理。递归的代码可以说相当简洁了~在面试中,一般不会让你用递归的方式去解决二叉树遍历的问题,那么来看看使用非递归的方式是如何实现遍历的。
换个角度想,使用非递归的方式实现二叉树的遍历实际上就是自己手动实现递归压栈的过程,所以一般需要栈等数据结构来辅助实现。

先序遍历:首先把头结点压栈,然后当栈不为空时执行循环,循环里面的逻辑为:弹出栈顶元素,如果弹出的结点有左右孩子,则依次压入右孩子结点和左孩子结点。代码如下:

public static List<Integer> preorderTraversal_v2(TreeNode root) {
        List<Integer> list = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();
        if (root == null) return list;
        stack.push(root);
        while (!stack.isEmpty()) {
            root = stack.pop();
            list.add(root.val);
            if (root.right != null) {
                stack.push(root.right);
            }
            if (root.left != null) {
                stack.push(root.left);
            }
        }
        return list;
    }

中序遍历:分两种情况:(1)只要当前结点有左孩子,一直压栈,直到压完子树的左边界;(2)第一种情况除外的情况。弹出栈顶结点,然后指向弹出结点的右孩子。代码及重要注释如下:

 public static List<Integer> inorderTraversal(TreeNode root) {

        Stack<TreeNode>  stack = new Stack<TreeNode>();
        List<Integer> list = new ArrayList<Integer>();
        if(root == null){
            return list;
        }
        while (!stack.isEmpty() || root != null){
        //循环终止的条件是栈为空,且当前结点也为空
            if(root!=null){
            //如果结点不为空,则将二叉树的左边界全部压栈
                stack.push(root);
                root = root.left;
            }
            else{
            //如果结点为空(也就是说上一个结点没有左孩子或者没有右孩子)
                 root =  stack.pop();//弹出当前结点
                 list.add(root.val);
                root = root.right;//进而指向弹出结点的右结孩子
            }
        }
        return list;
    }

后序遍历:对于一棵二叉树来说,后序遍历的顺序为 左→右→中,而对于上面的先序遍历非递归方式中,结点入栈的顺序为中→右→左,所以后续遍历非递归方式的一个比较易于理解的做法就是从先序遍历中寻找灵感。使用两个栈,栈s1为逻辑处理中的辅助栈,栈s1弹出的所有元素压入栈2。栈2出栈的顺序即为后序遍历的顺序。代码如下:

public static List<Integer> postorderTraversal_v1(TreeNode root) {
        List list = new ArrayList();
        Stack<TreeNode> stack1 = new Stack<TreeNode>();
        Stack<TreeNode> stack2 = new Stack<TreeNode>();
        if (root == null) return list;
        stack1.push(root);
        while (!stack1.isEmpty()) {
            root = stack1.pop();
            stack2.add(root);
            if (root.left != null) {
                stack1.push(root.left);
            }
            if (root.right != null) {
                stack1.push(root.right);
            }
        }
        while (!stack2.isEmpty()) {
            list.add(stack2.pop().val);
        }
        return list;
    }

总结:对于递归的方式,要点是要抓住递归的终止条件。对于非递归方式的遍历,关键是要明白结点进栈、出栈的顺序以及何时进栈、何时出栈。

你可能感兴趣的:(LeetCode)