LeetCode练习day3-树相关

LeetCode103 二叉树的锯齿形层序遍历

题目详情

给你二叉树的根节点 root ,返回其节点值的 锯齿形层序遍历 。(即先从左往右,再从右往左进行下一层遍历,以此类推,层与层之间交替进行)。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:[[3],[20,9],[15,7]]
示例 2:

输入:root = [1]
输出:[[1]]
示例 3:

输入:root = []
输出:[]

提示:

树中节点数目在范围 [0, 2000] 内
-100 <= Node.val <= 100

代码
class LeetCode103 {
    public static void main(String[] args) {
        System.out.println(new Solution().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
        System.out.println(new Solution2().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
        System.out.println(new Solution3().zigzagLevelOrder(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
    }

    /*
    算法

    实现 BFS 的几种算法。
    使用两层嵌套循环。外层循环迭代树的层级,内层循环迭代每层上的节点。
    也可以使用一层循环实现 BFS。将要访问的节点添加到队列中,使用 分隔符(例如:空节点)把不同层的节点分隔开。分隔符表示一层结束和新一层开始。
    这里采用第二种方法。在此算法的基础上,借助双端队列实现锯齿形顺序。在每一层,使用一个空的双端队列保存该层所有的节点。根据每一层的访问顺序,即从左到右或从右到左,决定从双端队列的哪一端插入节点。
     */
    static class Solution {
        public List> zigzagLevelOrder(TreeNode root) {
            if (root == null) {
                return new ArrayList<>();
            }
            List> results = new ArrayList<>();
            //添加带有分隔符的根元素以启动 BFS 循环
            LinkedList node_queue = new LinkedList<>();
            node_queue.addLast(root);
            node_queue.addLast(null);

            LinkedList level_list = new LinkedList<>();
            boolean is_order_left = true;

            while (node_queue.size() > 0) {
                TreeNode curr_node = node_queue.pollFirst();
                if (curr_node != null) {
                    if (is_order_left) {
                        level_list.addLast(curr_node.val);
                    } else {
                        level_list.addFirst(curr_node.val);
                    }

                    if (curr_node.left != null) {
                        node_queue.addLast(curr_node.left);
                    }
                    if (curr_node.right != null) {
                        node_queue.addLast(curr_node.right);
                    }
                } else {
                    //完成了一层的扫描
                    results.add(level_list);
                    level_list = new LinkedList<>();
                    //为下一层做准备
                    if (node_queue.size() > 0)
                        node_queue.addLast(null);
                    is_order_left = !is_order_left;
                }
            }
            return results;
        }
    }

    /*
    方法一:广度优先遍历
    此题是「102. 二叉树的层序遍历」的变种,最后输出的要求有所变化,要求我们按层数的奇偶来决定每一层的输出顺序。
    规定二叉树的根节点为第 00 层,如果当前层数是偶数,从左至右输出当前层的节点值,否则,从右至左输出当前层的节点值。

    我们依然可以沿用第 102 题的思想,修改广度优先搜索,对树进行逐层遍历,用队列维护当前层的所有元素,当队列不为空的时候,
    求得当前队列的长度 \textit{size}size,每次从队列中取出 \textit{size}size 个元素进行拓展,然后进行下一次迭代。
     */
    static class Solution2 {
        public List> zigzagLevelOrder(TreeNode root) {
            List> ans = new LinkedList>();
            if (root == null) {
                return ans;
            }
            Queue nodeQueue = new ArrayDeque();
            nodeQueue.offer(root);
            boolean isOrderLeft = true;

            while (!nodeQueue.isEmpty()) {
                Deque levelList = new LinkedList();
                int size = nodeQueue.size();
                for (int i = 0; i < size; ++i) {
                    TreeNode curNode = nodeQueue.poll();
                    if (isOrderLeft) {
                        levelList.offerLast(curNode.val);
                    } else {
                        levelList.offerFirst(curNode.val);
                    }
                    if (curNode.left != null) {
                        nodeQueue.offer(curNode.left);
                    }
                    if (curNode.right != null) {
                        nodeQueue.offer(curNode.right);
                    }
                }
                ans.add(new LinkedList(levelList));
                isOrderLeft = !isOrderLeft;
            }
            return ans;
        }
    }

    /*
    递归实现-深度优先遍历
    - 相同层序的节点归入同一个数组
    - 传入辅助的level参数,决定层序
     */
    static class Solution3 {
        private void dfs(TreeNode node, int level, List> res) {
            if (level == res.size()) {
                LinkedList newLevel = new LinkedList();
                newLevel.add(node.val);
                res.add(newLevel);
            } else {
                if (level % 2 == 0) {
                    res.get(level).add(node.val);
                } else {
                    res.get(level).add(0, node.val);
                }
            }
            if (node.left != null) {
                dfs(node.left, level + 1, res);
            }
            if (node.right != null) {
                dfs(node.right, level + 1, res);
            }
        }

        public List> zigzagLevelOrder(TreeNode root) {
            List> res = new ArrayList<>();
            if (root == null) {
                return res;
            }
            dfs(root, 0, res);
            return res;
        }
    }
}

LeetCode106 从中序与后序遍历序列构造二叉树

题目详情

给定两个整数数组 inorder 和 postorder ,其中 inorder 是二叉树的中序遍历, postorder 是同一棵树的后序遍历,请你构造并返回这颗 二叉树 。

示例 1:

输入:inorder = [9,3,15,20,7], postorder = [9,15,7,20,3]
输出:[3,9,20,null,null,15,7]
示例 2:

输入:inorder = [-1], postorder = [-1]
输出:[-1]

提示:

1 <= inorder.length <= 3000
postorder.length == inorder.length
-3000 <= inorder[i], postorder[i] <= 3000
inorder 和 postorder 都由 不同 的值组成
postorder 中每一个值都在 inorder 中
inorder 保证是树的中序遍历
postorder 保证是树的后序遍历

代码
class LeetCode106 {
    public static void main(String[] args) {
        TreeNode.prettyPrintTree(new Solution().buildTree(new int[]{9, 3, 15, 20, 7}, new int[]{9, 15, 7, 20, 3}));
        TreeNode.prettyPrintTree(new Solution2().buildTree(new int[]{9, 3, 15, 20, 7}, new int[]{9, 15, 7, 20, 3}));
    }

    /*
    递归
     */
    static class Solution {
        int post_idx;
        int[] postorder;
        int[] inorder;
        Map idx_map = new HashMap();

        public TreeNode helper(int in_left, int in_right) {
            // 如果这里没有节点构造二叉树了,就结束
            if (in_left > in_right) {
                return null;
            }

            // 选择 post_idx 位置的元素作为当前子树根节点
            int root_val = postorder[post_idx];
            TreeNode root = new TreeNode(root_val);

            // 根据 root 所在位置分成左右两棵子树
            int index = idx_map.get(root_val);

            // 下标减一
            post_idx--;
            // 构造右子树
            root.right = helper(index + 1, in_right);
            // 构造左子树
            root.left = helper(in_left, index - 1);
            return root;
        }

        public TreeNode buildTree(int[] inorder, int[] postorder) {
            this.postorder = postorder;
            this.inorder = inorder;
            // 从后序遍历的最后一个元素开始
            post_idx = postorder.length - 1;

            // 建立(元素,下标)键值对的哈希表
            int idx = 0;
            for (Integer val : inorder) {
                idx_map.put(val, idx++);
            }

            return helper(0, inorder.length - 1);
        }
    }

    /*
    迭代
     */
    static class Solution2 {
        public TreeNode buildTree(int[] inorder, int[] postorder) {
            if (postorder == null || postorder.length == 0) {
                return null;
            }
            TreeNode root = new TreeNode(postorder[postorder.length - 1]);
            Deque stack = new LinkedList();
            stack.push(root);
            int inorderIndex = inorder.length - 1;
            for (int i = postorder.length - 2; i >= 0; i--) {
                int postorderVal = postorder[i];
                TreeNode node = stack.peek();
                if (node.val != inorder[inorderIndex]) {
                    node.right = new TreeNode(postorderVal);
                    stack.push(node.right);
                } else {
                    while (!stack.isEmpty() && stack.peek().val == inorder[inorderIndex]) {
                        node = stack.pop();
                        inorderIndex--;
                    }
                    node.left = new TreeNode(postorderVal);
                    stack.push(node.left);
                }
            }
            return root;
        }
    }
}

LeetCode108 将有序数组转换为二叉树

题目详情

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。
高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

示例 1:

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9] 也将被视为正确答案:

示例 2:

输入:nums = [1,3]
输出:[3,1]
解释:[1,null,3] 和 [3,1] 都是高度平衡二叉搜索树。

提示:

1 <= nums.length <= 104
-104 <= nums[i] <= 104
nums 按 严格递增 顺序排列

代码
class LeetCode108 {
    public static void main(String[] args) {
        TreeNode.prettyPrintTree(new Solution().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
        TreeNode.prettyPrintTree(new Solution2().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
        TreeNode.prettyPrintTree(new Solution3().sortedArrayToBST(new int[]{-10, -3, 0, 5, 9}));
    }

    /*
    方法一:中序遍历,总是选择中间位置左边的数字作为根节点
     */

    static class Solution {
        public TreeNode sortedArrayToBST(int[] nums) {
            return helper(nums, 0, nums.length - 1);
        }

        public TreeNode helper(int[] nums, int left, int right) {
            if (left > right) {
                return null;
            }
            // 总是选择中间位置左边的数字作为根节点
            int mid = (left + right) / 2;

            TreeNode root = new TreeNode(nums[mid]);
            root.left = helper(nums, left, mid - 1);
            root.right = helper(nums, mid + 1, right);
            return root;
        }
    }

    /*
    方法二:中序遍历,总是选择中间位置右边的数字作为根节点
     */
    static class Solution2 {
        public TreeNode sortedArrayToBST(int[] nums) {
            return helper(nums, 0, nums.length - 1);
        }

        public TreeNode helper(int[] nums, int left, int right) {
            if (left > right) {
                return null;
            }

            // 总是选择中间位置右边的数字作为根节点
            int mid = (left + right + 1) / 2;

            TreeNode root = new TreeNode(nums[mid]);
            root.left = helper(nums, left, mid - 1);
            root.right = helper(nums, mid + 1, right);
            return root;
        }
    }

    /*
    方法三:中序遍历,选择任意一个中间位置数字作为根节点
     */
    static class Solution3 {
        Random rand = new Random();

        public TreeNode sortedArrayToBST(int[] nums) {
            return helper(nums, 0, nums.length - 1);
        }

        public TreeNode helper(int[] nums, int left, int right) {
            if (left > right) {
                return null;
            }
            // 选择任意一个中间位置数字作为根节点
            int mid = (left + right + rand.nextInt(2)) / 2;

            TreeNode root = new TreeNode(nums[mid]);
            root.left = helper(nums, left, mid - 1);
            root.right = helper(nums, mid + 1, right);
            return root;
        }
    }
}

LeetCode110 平衡二叉树

题目详情

给定一个二叉树,判断它是否是高度平衡的二叉树。
本题中,一棵高度平衡二叉树定义为:
一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。

示例 1:

输入:root = [3,9,20,null,null,15,7]
输出:true
示例 2:

输入:root = [1,2,2,3,3,null,null,4,4]
输出:false
示例 3:

输入:root = []
输出:true

提示:

树中的节点数在范围 [0, 5000] 内
-104 <= Node.val <= 104

代码
class LeetCode110 {
    public static void main(String[] args) {
        System.out.println(new Solution().isBalanced(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
        System.out.println(new Solution2().isBalanced(TreeNode.buildBinaryTree(new Integer[]{3, 9, 20, null, null, 15, 7})));
    }

    /*
    方法一:自顶向下的递归
     */
    static class Solution {
        public boolean isBalanced(TreeNode root) {
            if (root == null) {
                return true;
            } else {
                return Math.abs(height(root.left) - height(root.right)) <= 1 && isBalanced(root.left) && isBalanced(root.right);
            }
        }

        public int height(TreeNode root) {
            if (root == null) {
                return 0;
            } else {
                return Math.max(height(root.left), height(root.right)) + 1;
            }
        }
    }

    /*
    方法二:自底向上的递归
     */
    static class Solution2 {
        public boolean isBalanced(TreeNode root) {
            return height(root) >= 0;
        }

        public int height(TreeNode root) {
            if (root == null) {
                return 0;
            }
            int leftHeight = height(root.left);
            int rightHeight = height(root.right);
            if (leftHeight == -1 || rightHeight == -1 || Math.abs(leftHeight - rightHeight) > 1) {
                return -1;
            } else {
                return Math.max(leftHeight, rightHeight) + 1;
            }
        }
    }
}


LeetCode112 路径总和

题目详情

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,null,1], targetSum = 22
输出:true
解释:等于目标和的根节点到叶节点路径如上图所示。
示例 2:

输入:root = [1,2,3], targetSum = 5
输出:false
解释:树中存在两条根节点到叶子节点的路径:
(1 --> 2): 和为 3
(1 --> 3): 和为 4
不存在 sum = 5 的根节点到叶子节点的路径。
示例 3:

输入:root = [], targetSum = 0
输出:false
解释:由于树是空的,所以不存在根节点到叶子节点的路径。

提示:

树中节点的数目在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000

代码
class LeetCode112 {
    public static void main(String[] args) {
        System.out.println(new Solution().hasPathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1}), 22));
        System.out.println(new Solution2().hasPathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, null, 1}), 22));
    }

    /*
    方法一:广度优先搜索
    首先我们可以想到使用广度优先搜索的方式,记录从根节点到当前节点的路径和,以防止重复计算。
    这样我们使用两个队列,分别存储将要遍历的节点,以及根节点到这些节点的路径和即可。
     */
    static class Solution {
        public boolean hasPathSum(TreeNode root, int sum) {
            if (root == null) {
                return false;
            }
            Queue queNode = new LinkedList();
            Queue queVal = new LinkedList();
            queNode.offer(root);
            queVal.offer(root.val);
            while (!queNode.isEmpty()) {
                TreeNode now = queNode.poll();
                int temp = queVal.poll();
                if (now.left == null && now.right == null) {
                    if (temp == sum) {
                        return true;
                    }
                    continue;
                }
                if (now.left != null) {
                    queNode.offer(now.left);
                    queVal.offer(now.left.val + temp);
                }
                if (now.right != null) {
                    queNode.offer(now.right);
                    queVal.offer(now.right.val + temp);
                }
            }
            return false;
        }
    }

    /*
    方法二:递归
     */
    static class Solution2 {
        public boolean hasPathSum(TreeNode root, int sum) {
            if (root == null) {
                return false;
            }
            if (root.left == null && root.right == null) {
                return sum == root.val;
            }
            return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val);
        }
    }
}

LeetCode113 路径总和-二

题目详情

给你二叉树的根节点 root 和一个整数目标和 targetSum ,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。

示例 1:

输入:root = [5,4,8,11,null,13,4,7,2,null,null,5,1], targetSum = 22
输出:[[5,4,11,2],[5,8,4,5]]
示例 2:

输入:root = [1,2,3], targetSum = 5
输出:[]
示例 3:

输入:root = [1,2], targetSum = 0
输出:[]

提示:

树中节点总数在范围 [0, 5000] 内
-1000 <= Node.val <= 1000
-1000 <= targetSum <= 1000

代码
class LeetCode113 {
    public static void main(String[] args) {
        System.out.println(new Solution().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
        System.out.println(new Solution2().dfs(TreeNode.deserialize("[5,4,8,11,null,13,4,7,2,null,null,5,1]"), 22));
        System.out.println(new Solution3().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
        System.out.println(new Solution4().pathSum(TreeNode.buildBinaryTree(new Integer[]{5, 4, 8, 11, null, 13, 4, 7, 2, null, null, 5, 1}), 22));
    }

    /*
    这一题是很典型的回溯算法
    思路:
    从根节点出发,使用一个temp变量和List容器记录走过路径
    在走该条路径之前将节点的值加入List容器,走完之后(返回之后)将加入的值移除
    当节点为空时,返回继续查找
    当节点的左孩子和右孩子都为空并且temp变量等于sum时,说明这个节点是叶子节点且找到了一条路径,将List加入答案中
     */
    static class Solution {
        public List> pathSum(TreeNode root, int sum) {
            List> ans = new ArrayList<>();
            List path = new ArrayList<>();
            pathSum(ans, path, root, 0, sum);
            return ans;
        }

        public void pathSum(List> ans, List path, TreeNode root, int temp, int sum) {
            if (root == null) {
                return;
            }
            //叶子节点 且 匹配
            if (root.left == null && root.right == null && temp + root.val == sum) {
                path.add(root.val);/*添加该叶子节点*/
                ans.add(new ArrayList<>(path));//加入结果数组
                path.remove(path.size() - 1);//删除最后一项 相当与恢复到叶子节点上一层位置
                return;
            }
            path.add(root.val);/*添加该节点*/
            pathSum(ans, path, root.left, temp + root.val, sum);
            pathSum(ans, path, root.right, temp + root.val, sum);
            path.remove(path.size() - 1);/*回到该节点*/
        }
    }


    static class Solution2 {
        public List> dfs(TreeNode root, int sum) {
            List> res = new ArrayList<>();
            if (root == null) {
                return res;
            }
            // Java 文档中 Stack 类建议使用 Deque 代替 Stack,注意:只使用栈的相关接口
            Deque path = new ArrayDeque<>();
            dfs(root, sum, path, res);
            return res;
        }

        private void dfs(TreeNode node, int sum, Deque path, List> res) {
            if (node == null) {
                return;
            }
            if (node.val == sum && node.left == null && node.right == null) {
                path.addLast(node.val);
                res.add(new ArrayList<>(path));
                path.removeLast();
                return;
            }
            path.addLast(node.val);
            dfs(node.left, sum - node.val, path, res);
            dfs(node.right, sum - node.val, path, res);
            path.removeLast();
        }
    }

    /*
    方法一:深度优先搜索
     */
    static class Solution3 {
        List> ret = new LinkedList>();
        Deque path = new LinkedList();

        public List> pathSum(TreeNode root, int targetSum) {
            dfs(root, targetSum);
            return ret;
        }

        public void dfs(TreeNode root, int targetSum) {
            if (root == null) {
                return;
            }
            path.offerLast(root.val);
            targetSum -= root.val;
            if (root.left == null && root.right == null && targetSum == 0) {
                ret.add(new LinkedList(path));
            }
            dfs(root.left, targetSum);
            dfs(root.right, targetSum);
            path.pollLast();
        }
    }
    /*
    方法二:广度优先搜索
     */

    static class Solution4 {
        List> ret = new LinkedList>();
        Map map = new HashMap();

        public List> pathSum(TreeNode root, int targetSum) {
            if (root == null) {
                return ret;
            }
            Queue queueNode = new LinkedList();
            Queue queueSum = new LinkedList();
            queueNode.offer(root);
            queueSum.offer(0);

            while (!queueNode.isEmpty()) {
                TreeNode node = queueNode.poll();
                int rec = queueSum.poll() + node.val;

                if (node.left == null && node.right == null) {
                    if (rec == targetSum) {
                        getPath(node);
                    }
                } else {
                    if (node.left != null) {
                        map.put(node.left, node);
                        queueNode.offer(node.left);
                        queueSum.offer(rec);
                    }
                    if (node.right != null) {
                        map.put(node.right, node);
                        queueNode.offer(node.right);
                        queueSum.offer(rec);
                    }
                }
            }
            return ret;
        }

        public void getPath(TreeNode node) {
            List temp = new LinkedList();
            while (node != null) {
                temp.add(node.val);
                node = map.get(node);
            }
            Collections.reverse(temp);
            ret.add(new LinkedList(temp));
        }
    }
}

LeetCode116 填充每个节点的下一个右侧节点指针

题目详情

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:
struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

示例 1:

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。
示例 2:

输入:root = []
输出:[]

提示:

树中节点的数量在 [0, 212 - 1] 范围内
-1000 <= node.val <= 1000

进阶:

你只能使用常量级额外空间。
使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

代码
class LeetCode116 {
    public static void main(String[] args) {
        Node.printNode(new Solution().connect(new Node(3)));
        Node.printNode(new Solution2().connect(new Node(3)));
    }

    /*
    方法一:层次遍历
     */
    static class Solution {
        public Node connect(Node root) {
            if (root == null) {
                return root;
            }
            // 初始化队列同时将第一层节点加入队列中,即根节点
            Queue queue = new LinkedList();
            queue.add(root);
            // 外层的 while 循环迭代的是层数
            while (!queue.isEmpty()) {
                // 记录当前队列大小
                int size = queue.size();
                // 遍历这一层的所有节点
                for (int i = 0; i < size; i++) {
                    // 从队首取出元素
                    Node node = queue.poll();
                    // 连接
                    if (i < size - 1) {
                        node.next = queue.peek();
                    }
                    // 拓展下一层节点
                    if (node.left != null) {
                        queue.add(node.left);
                    }
                    if (node.right != null) {
                        queue.add(node.right);
                    }
                }
            }
            // 返回根节点
            return root;
        }
    }

    /*
    方法二:使用已建立的
     */
    static class Solution2 {
        public Node connect(Node root) {
            if (root == null) {
                return root;
            }
            // 从根节点开始
            Node leftmost = root;
            while (leftmost.left != null) {
                // 遍历这一层节点组织成的链表,为下一层的节点更新 next 指针
                Node head = leftmost;
                while (head != null) {
                    // CONNECTION 1
                    head.left.next = head.right;
                    // CONNECTION 2
                    if (head.next != null) {
                        head.right.next = head.next.left;
                    }
                    // 指针向后移动
                    head = head.next;
                }
                // 去下一层的最左的节点
                leftmost = leftmost.left;
            }
            return root;
        }
    }
}

LeetCode230 二叉搜索树中第K小的元素

题目详情

给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 个最小元素(从 1 开始计数)。

示例 1:

输入:root = [3,1,4,null,2], k = 1
输出:1
示例 2:

输入:root = [5,3,6,2,4,null,null,1], k = 3
输出:3

提示:

树中的节点数为 n 。
1 <= k <= n <= 104
0 <= Node.val <= 104

进阶:如果二叉搜索树经常被修改(插入/删除操作)并且你需要频繁地查找第 k 小的值,你将如何优化算法?

代码
class LeetCode230 {
    public static void main(String[] args) {
        //“掌握解法一,能讲解法二,知道AVL”,够用
        System.out.println(new Solution().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
        System.out.println(new Solution2().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
        System.out.println(new Solution3().kthSmallest(TreeNode.buildBinaryTree(new Integer[]{5, 3, 6, 2, 4, null, null, 1}), 3));
    }

    /*
    方法一:中序遍历
     */
    static class Solution {
        public int kthSmallest(TreeNode root, int k) {
            Deque stack = new ArrayDeque();
            while (root != null || !stack.isEmpty()) {
                while (root != null) {
                    stack.push(root);
                    root = root.left;
                }
                root = stack.pop();
                --k;
                if (k == 0) {
                    break;
                }
                root = root.right;
            }
            return root.val;
        }
    }

    /*
    方法二:记录子树的结点数
     */
    static class Solution2 {
        public int kthSmallest(TreeNode root, int k) {
            MyBst bst = new MyBst(root);
            return bst.kthSmallest(k);
        }
    }

    static class MyBst {
        TreeNode root;
        Map nodeNum;

        public MyBst(TreeNode root) {
            this.root = root;
            this.nodeNum = new HashMap();
            countNodeNum(root);
        }

        // 返回二叉搜索树中第k小的元素
        public int kthSmallest(int k) {
            TreeNode node = root;
            while (node != null) {
                int left = getNodeNum(node.left);
                if (left < k - 1) {
                    node = node.right;
                    k -= left + 1;
                } else if (left == k - 1) {
                    break;
                } else {
                    node = node.left;
                }
            }
            return node.val;
        }

        // 统计以node为根结点的子树的结点数
        private int countNodeNum(TreeNode node) {
            if (node == null) {
                return 0;
            }
            nodeNum.put(node, 1 + countNodeNum(node.left) + countNodeNum(node.right));
            return nodeNum.get(node);
        }

        // 获取以node为根结点的子树的结点数
        private int getNodeNum(TreeNode node) {
            return nodeNum.getOrDefault(node, 0);
        }
    }
}

你可能感兴趣的:(LeetCode练习day3-树相关)