美文网首页
LeetCode练习day3-树相关

LeetCode练习day3-树相关

作者: 码农朱同学 | 来源:发表于2022-08-27 11:46 被阅读0次

    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<List<Integer>> zigzagLevelOrder(TreeNode root) {
                if (root == null) {
                    return new ArrayList<>();
                }
                List<List<Integer>> results = new ArrayList<>();
                //添加带有分隔符的根元素以启动 BFS 循环
                LinkedList<TreeNode> node_queue = new LinkedList<>();
                node_queue.addLast(root);
                node_queue.addLast(null);
    
                LinkedList<Integer> 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<List<Integer>> zigzagLevelOrder(TreeNode root) {
                List<List<Integer>> ans = new LinkedList<List<Integer>>();
                if (root == null) {
                    return ans;
                }
                Queue<TreeNode> nodeQueue = new ArrayDeque<TreeNode>();
                nodeQueue.offer(root);
                boolean isOrderLeft = true;
    
                while (!nodeQueue.isEmpty()) {
                    Deque<Integer> levelList = new LinkedList<Integer>();
                    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<Integer>(levelList));
                    isOrderLeft = !isOrderLeft;
                }
                return ans;
            }
        }
    
        /*
        递归实现-深度优先遍历
        - 相同层序的节点归入同一个数组
        - 传入辅助的level参数,决定层序
         */
        static class Solution3 {
            private void dfs(TreeNode node, int level, List<List<Integer>> res) {
                if (level == res.size()) {
                    LinkedList<Integer> newLevel = new LinkedList<Integer>();
                    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<List<Integer>> zigzagLevelOrder(TreeNode root) {
                List<List<Integer>> 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<Integer, Integer> idx_map = new HashMap<Integer, Integer>();
    
            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<TreeNode> stack = new LinkedList<TreeNode>();
                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<TreeNode> queNode = new LinkedList<TreeNode>();
                Queue<Integer> queVal = new LinkedList<Integer>();
                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<List<Integer>> pathSum(TreeNode root, int sum) {
                List<List<Integer>> ans = new ArrayList<>();
                List<Integer> path = new ArrayList<>();
                pathSum(ans, path, root, 0, sum);
                return ans;
            }
    
            public void pathSum(List<List<Integer>> ans, List<Integer> 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<List<Integer>> dfs(TreeNode root, int sum) {
                List<List<Integer>> res = new ArrayList<>();
                if (root == null) {
                    return res;
                }
                // Java 文档中 Stack 类建议使用 Deque 代替 Stack,注意:只使用栈的相关接口
                Deque<Integer> path = new ArrayDeque<>();
                dfs(root, sum, path, res);
                return res;
            }
    
            private void dfs(TreeNode node, int sum, Deque<Integer> path, List<List<Integer>> 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<List<Integer>> ret = new LinkedList<List<Integer>>();
            Deque<Integer> path = new LinkedList<Integer>();
    
            public List<List<Integer>> 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<Integer>(path));
                }
                dfs(root.left, targetSum);
                dfs(root.right, targetSum);
                path.pollLast();
            }
        }
        /*
        方法二:广度优先搜索
         */
    
        static class Solution4 {
            List<List<Integer>> ret = new LinkedList<List<Integer>>();
            Map<TreeNode, TreeNode> map = new HashMap<TreeNode, TreeNode>();
    
            public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
                if (root == null) {
                    return ret;
                }
                Queue<TreeNode> queueNode = new LinkedList<TreeNode>();
                Queue<Integer> queueSum = new LinkedList<Integer>();
                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<Integer> temp = new LinkedList<Integer>();
                while (node != null) {
                    temp.add(node.val);
                    node = map.get(node);
                }
                Collections.reverse(temp);
                ret.add(new LinkedList<Integer>(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<Node> queue = new LinkedList<Node>();
                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<TreeNode> stack = new ArrayDeque<TreeNode>();
                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<TreeNode, Integer> nodeNum;
    
            public MyBst(TreeNode root) {
                this.root = root;
                this.nodeNum = new HashMap<TreeNode, Integer>();
                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-树相关

          本文链接:https://www.haomeiwen.com/subject/gmoagrtx.html