05-BinaryTree

暴力递归就是尝试

  1. 把问题转化为规模缩小了的同类问题的子问题
  2. 有明确的不需要继续进行递归的条件(base case)
  3. 有当得到了子问题的结果之后的决策过程
  4. 不记录每一个子问题的解

1. 先、中、后序遍历

package com.listao.algorithm.class06;

import org.junit.jupiter.api.Test;
import java.util.HashMap;

/**
 * 遍历二叉树
 * Traversal: 遍历
 */
public class _03_BinaryTree {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        public TreeNode(int v) {
            val = v;
        }
    }

    /**
     * 递归序
     * <p>
     * 每个节点在这个方法要经历三次
     * 1. 经历第一次就是前序
     * 2. 经历第二次就是中序
     * 3. 经历第三次就是后序
     */
    public static void f(TreeNode head) {
        if (head == null) {
            return;
        }
        // 1.
        f(head.left);
        // 2.
        f(head.right);
        // 3.
    }

    /**
     * 先序遍历
     */
    public static void pre(TreeNode head) {
        if (head == null) {
            return;
        }
        System.out.println(head.val);
        pre(head.left);
        pre(head.right);
    }

    /**
     * 中序遍历
     */
    public static void in(TreeNode head) {
        if (head == null) {
            return;
        }
        in(head.left);
        System.out.println(head.val);
        in(head.right);
    }

    /**
     * 后序遍历
     */
    public static void pos(TreeNode head) {
        if (head == null) {
            return;
        }
        pos(head.left);
        pos(head.right);
        System.out.println(head.val);
    }

    /**
     * 先:头 左 右
     * 中:左 头 右
     * 后:左 右 头
     */
    @Test
    public void traversal() {
        TreeNode head = new TreeNode(1);
        head.left = new TreeNode(2);
        head.right = new TreeNode(3);
        head.left.left = new TreeNode(4);
        head.left.right = new TreeNode(5);
        head.right.left = new TreeNode(6);
        head.right.right = new TreeNode(7);

        pre(head);
        System.out.println("========");
        in(head);
        System.out.println("========");
        pos(head);
        System.out.println("========");
    }

    // --------------------------- 两树一致 SameTree ---------------------------

    /**
     * 判断两个树一致
     * https://leetcode.com/problems/same-tree
     */
    public static boolean isSameTree(TreeNode p, TreeNode q) {
        // 一个为null,一个不为null
        if (p == null ^ q == null) {
            return false;
        }
        // 都为null
        if (p == null && q == null) {
            return true;
        }
        // 都不为空
        return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right);
    }

    // --------------------------- 镜像树 SymmetricTree ---------------------------

    /**
     * 镜像树为两棵互为镜像树的合并
     */
    public static boolean isSymmetric(TreeNode root) {
        return isMirror(root, root);
    }

    /**
     * 判断一棵树是否是镜面树
     * https://leetcode.com/problems/symmetric-tree
     */
    public static boolean isMirror(TreeNode h1, TreeNode h2) {
        if (h1 == null ^ h2 == null) {
            return false;
        }
        if (h1 == null && h2 == null) {
            return true;
        }
        return h1.val == h2.val && isMirror(h1.left, h2.right) && isMirror(h1.right, h2.left);
    }

    // --------------------------- 树最大高度 MaximumDepthOfBinaryTree ---------------------------

    /**
     * 树的最大高度
     * https://leetcode.com/problems/maximum-depth-of-binary-tree
     */
    public static int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 左树、右树中高度最大的 + 1
        return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }

    // --------------------------- 先序、中序遍历,重建一棵树 ---------------------------

    /**
     * 先序、中序遍历,重建一棵树
     * https://leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal
     */
    public static TreeNode buildTree1(int[] pre, int[] in) {
        if (pre == null || in == null || pre.length != in.length) {
            return null;
        }
        return f(pre, 0, pre.length - 1, in, 0, in.length - 1);
    }

    /**
     * 有一棵树,先序结果pre[L1...R1],中序结果in[L2...R2]
     * 请建出整棵树返回头节点
     */
    public static TreeNode f(int[] pre, int L1, int R1, int[] in, int L2, int R2) {
        // 左树为null,或右树为null(只有一边树)
        if (L1 > R1) {
            return null;
        }
        TreeNode head = new TreeNode(pre[L1]);
        if (L1 == R1) {
            return head;
        }
        // 1. 中序遍历中头节点位置(头节点值pre[L1])
        int find = L2;
        while (in[find] != pre[L1]) {
            find++;
        }
        head.left = f(pre, L1 + 1, L1 + (find - L2), in, L2, (find - 1));
        head.right = f(pre, L1 + (find - L2) + 1, R1, in, find + 1, R2);
        return head;
    }

    public static TreeNode buildTree2(int[] pre, int[] in) {
        if (pre == null || in == null || pre.length != in.length) {
            return null;
        }
        // 1. 统计中序遍历每个值在哪里
        HashMap<Integer, Integer> valueIndexMap = new HashMap<>();
        for (int i = 0; i < in.length; i++) {
            valueIndexMap.put(in[i], i);
        }
        return g(pre, 0, pre.length - 1, in, 0, in.length - 1, valueIndexMap);
    }

    /**
     * 空间换时间。省略遍历行为
     */
    public static TreeNode g(int[] pre, int L1, int R1, int[] in, int L2, int R2
            , HashMap<Integer, Integer> valueIndexMap) {
        if (L1 > R1) {
            return null;
        }
        TreeNode head = new TreeNode(pre[L1]);
        if (L1 == R1) {
            return head;
        }
        int find = valueIndexMap.get(pre[L1]);
        head.left = g(pre, L1 + 1, L1 + find - L2, in, L2, find - 1, valueIndexMap);
        head.right = g(pre, L1 + find - L2 + 1, R1, in, find + 1, R2, valueIndexMap);
        return head;
    }

}

































 

 

 









 












 












 









































 






















 













 
























 
 
 
 
 
 
 
 
 
 
 










 

 














 






2. 层序遍历

/**
 * 层序遍历(二叉树)
 * <p>
 * 1. queue的`size()`值即为2步骤执行次数
 * 2. 弹出节点,当前节点有左先加左,有右再加右
 * <p>
 * https://leetcode.com/problems/binary-tree-level-order-traversal-ii
 */
public class _01_BinaryTreeLevelOrderTraversal {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }
    }

    /**
     * [[4, 5, 7], [2, 3], [1]]
     */
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> ans = new LinkedList<>();
        if (root == null) {
            return ans;
        }
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        // 一次while就是一层
        while (!queue.isEmpty()) {
            // 本层元素个数
            int size = queue.size();
            List<Integer> curAns = new LinkedList<>();
            // `queue.size()`是动态变化的,必须取出来的int
            for (int i = 0; i < size; i++) {
                TreeNode curNode = queue.poll();
                curAns.add(curNode.val);
                // 弹出节点,左右节点按顺序加入
                if (curNode.left != null) {
                    queue.add(curNode.left);
                }
                if (curNode.right != null) {
                    queue.add(curNode.right);
                }
            }
            // 逆序
            ans.add(0, curAns);
        }
        return ans;
    }

    /**
     * 1. 栈结构,java本身Stack可以直接实例化
     * 2. 双端队列LinkedList也具备stack功能
     * 3. 数组 int[]也具备stack功能
     * 效率大约:int[] > stack > LinkedList
     */
    @Test
    public void stack() {

        // ------------- Stack -------------
        long l = System.currentTimeMillis();
        int times = 10_000_000;

        Stack<Integer> stack = new Stack<>();
        for (int i = 0; i < times; i++) {
            stack.add(i);
        }
        while (!stack.empty()) {
            int pop = stack.pop();
        }

        // ------------- LinkedList实现Stack -------------
        long stackTime = System.currentTimeMillis();
        System.out.println("stack,时间:" + (stackTime - l));

        LinkedList<Integer> list = new LinkedList<>();
        for (int i = 0; i < times; i++) {
            list.add(i);
        }
        while (!list.isEmpty()) {
            int pop = list.pollLast();
        }

        long linkedListTime = System.currentTimeMillis();
        System.out.println("LinkedList,时间:" + (linkedListTime - stackTime));

        // ------------- int[]实现Stack -------------
        int[] ints = new int[times];

        int index = 0;
        for (int i = 0; i < times; i++) {
            ints[index++] = i;
        }
        while (index != 0) {
            int anInt = ints[--index];
        }
        long end = System.currentTimeMillis();
        System.out.println("int[],时间:" + (end - linkedListTime));
    }

}




























 







 
 
 
 
 
 
 
 
 
 
 

























































3. 平衡二叉树

/**
 * 判断是否是平衡二叉树
 * 平衡树:左、右子树的高度差<=1
 * <p>
 * https://leetcode.com/problems/balanced-binary-tree
 */
public class _02_BalancedBinaryTree {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }
    }

    /**
     * 1. 树是否平衡
     * 2. 高度
     */
    public static class Info {
        public boolean isBalanced;
        public int height;

        public Info(boolean i, int h) {
            isBalanced = i;
            height = h;
        }
    }

    public static boolean isBalanced(TreeNode root) {
        return process(root).isBalanced;
    }

    public static Info process(TreeNode root) {
        if (root == null) {
            return new Info(true, 0);
        }
        Info leftInfo = process(root.left);
        Info rightInfo = process(root.right);
        int height = Math.max(leftInfo.height, rightInfo.height) + 1;
        boolean isBalanced = leftInfo.isBalanced && rightInfo.isBalanced
                && Math.abs(leftInfo.height - rightInfo.height) < 2;
        return new Info(isBalanced, height);
    }

}





































 
 
 
 
 
 
 
 




4. 二叉搜索树

/**
 * 判断是否是二叉搜索树
 * <p>
 * 1. 中序遍历,严格递增
 * 2. 左节点小,右节点大
 */
public class _03_BinarySearchTree {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }
    }

    public static class Info {
        public boolean isBST;
        // 返回值左右树没有产生调用差异
        public int max;
        public int min;

        public Info(boolean is, int ma, int mi) {
            isBST = is;
            max = ma;
            min = mi;
        }
    }

    /**
     * 手写
     */
    public static Info process(TreeNode x) {
        // baseCase
        if (x == null) {
            return null;
        }
        // 左右树info
        Info leftInfo = process(x.left);
        Info rightInfo = process(x.right);
        int max = x.val;
        int min = x.val;
        // max min归位
        if (leftInfo != null) {
            max = Math.max(leftInfo.max, max);
            min = Math.min(leftInfo.min, min);
        }
        if (rightInfo != null) {
            max = Math.max(rightInfo.max, max);
            min = Math.min(rightInfo.min, min);
        }

        boolean isBST = true;
        if (leftInfo != null && !leftInfo.isBST) {
            isBST = false;
        }
        if (rightInfo != null && !rightInfo.isBST) {
            isBST = false;
        }
        // 判断 左 < 中 < 右
        boolean leftMaxLessX = leftInfo == null ? true : (leftInfo.max < x.val);
        boolean rightMinMoreX = rightInfo == null ? true : (rightInfo.min > x.val);
        if (!(leftMaxLessX && rightMinMoreX)) {
            isBST = false;
        }
        return new Info(isBST, max, min);
    }

}



































 



 




 
















 









5. 路径和

/**
 * 路径和问题
 * 从rootNode到leafNode,一条路径上node和,等于指定值
 * <p>
 * https://leetcode.com/problems/path-sum
 */
public class _04_PathSum {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }
    }

    public static boolean isSum = false;

    public static boolean hasPathSum(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        // 起点,将全局变量false
        isSum = false;
        process(root, 0, sum);
        return isSum;
    }

    public static void process(TreeNode x, int preSum, int sum) {
        // base case
        if (x.left == null && x.right == null) {
            if (x.val + preSum == sum) {
                isSum = true;
            }
            return;
        }
        // x是非叶节点
        preSum += x.val;
        if (x.left != null) {
            process(x.left, preSum, sum);
        }
        if (x.right != null) {
            process(x.right, preSum, sum);
        }
    }

    // ------------------------------------------------------------------------

    /**
     * 标准版
     */
    public static boolean hasPathSum2(TreeNode root, int sum) {
        if (root == null) {
            return false;
        }
        return process2(root, sum);
    }

    /**
     * 标准版
     */
    public static boolean process2(TreeNode root, int rest) {
        if (root.left == null && root.right == null) {
            return root.val == rest;
        }
        boolean ans = root.left != null && process2(root.left, rest - root.val);
        ans |= root.right != null && process2(root.right, rest - root.val);
        return ans;
    }

}































 
 
 
 
 
 
 
 
 
 
 
 
 
 
 


















 
 
 
 
 
 



/**
 * 收集达标路径和
 * <p>
 * https://leetcode.com/problems/path-sum-ii
 */
public class _05_PathSums {

    public static class TreeNode {
        public int val;
        public TreeNode left;
        public TreeNode right;

        TreeNode(int val) {
            this.val = val;
        }
    }

    public static List<List<Integer>> pathSum(TreeNode root, int sum) {
        List<List<Integer>> ans = new ArrayList<>();
        if (root == null) {
            return ans;
        }
        ArrayList<Integer> path = new ArrayList<>();
        process(root, path, 0, sum, ans);
        return ans;
    }

    public static void process(TreeNode x, List<Integer> path, int preSum, int sum, List<List<Integer>> ans) {
        if (x.left == null && x.right == null) { // leafNode
            // 结束条件
            if (preSum + x.val == sum) {
                path.add(x.val);
                ans.add(new ArrayList<>(path));
                // 处理完leafNode,返回其到父node
                path.remove(path.size() - 1);
            }
            return;
        }
        // 非leafNode
        path.add(x.val);
        preSum += x.val;
        if (x.left != null) {
            process(x.left, path, preSum, sum, ans);
        }
        if (x.right != null) {
            process(x.right, path, preSum, sum, ans);
        }
        // 处理完Node,返回其到父node
        path.remove(path.size() - 1);
    }

}