代码随想录算法训练营第二十三天| 669. 修剪二叉搜索树、108. 将有序数组转换为二叉搜索树、538. 把二叉搜索树转换为累加树

[LeetCode] 669. 修剪二叉搜索树

[LeetCode] 669. 修剪二叉搜索树 文章说明

[LeetCode] 669. 修剪二叉搜索树 视频说明
 

题目:

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

示例 1:

输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]

示例 2:

输入:root = [3,0,4,null,2,null,null,1], low = 1, high = 3
输出:[3,2,null,1]

提示:

  • 树中节点数在范围 [1, 10^4]
  • 0 <= Node.val <= 10^4
  • 树中每个节点的值都是 唯一
  • 题目数据保证输入是一棵有效的二叉搜索树
  • 0 <= low <= high <= 10^4

[LeetCode] 669. 修剪二叉搜索树

自己看到题目的第一想法

    1. 遍历二叉树, 遇到不符合的节点, 则删除该节点.

看完代码随想录之后的想法

    一开始我删除节点的时候, 使用后续遍历, 从叶子结点开始处理, 一个一个递归上去. 然而这样就无法利用到二叉搜索树的特性: 如果当前节点不在范围内, 那他的左子树或者右子树, 有一个就一定不在范围内.

// 递归法: 耗时版本.
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return root;
        }
        root.left = trimBST(root.left, low, high);// 删除所有左子树中不合适的节点
        root.right = trimBST(root.right, low, high);// 删除所有右子树中不合适的节点
        // 确认一下当前节点是否满足条件. 
        // 如果当前节点不满足条件的话, 他的左子树或者右子树是不需要处理的.
        // 然而这里用后续遍历的话, 递归过程中就要处理原本已知不满足条件的节点.
        if (root.val < low) {
            root = root.right;
        } else if (root.val > high) {
            root = root.left;
        }
        return root;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return root;
        }
        if (root.val < low) {
            // 如果当前节点不满足条件, 则向下寻找满足条件的节点
            // 这里的 return 会自动删除当前节点, 并寻找最近的合适的节点
            return trimBST(root.right, low, high);
        } else if (root.val > high) {
            // 如果当前节点不满足条件, 则向下寻找满足条件的节点
            // 这里的 return 会自动删除当前节点, 并寻找最近的合适的节点
            return trimBST(root.left, low, high);
        }
        // 走到这里说明当前的节点是合适的, 但是要处理一下左右子节点
        // 将左右子树中不合适的节点删除
        root.left = trimBST(root.left, low, high);
        root.right = trimBST(root.right, low, high);
        
        return root;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 迭代法
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) {
            return null;
        }
        while (root != null && (root.val < low || root.val > high)) {
            if (root.val < low) {
                root = root.right;
            } else {
                root = root.left;
            }
        }
        if (root == null) {
            return null;
        }
        TreeNode node = root;
        while (node.left != null) {
            if (node.left.val < low) {
                node.left = node.left.right;// 这里直接舍弃一半真的太舒服了
            } else if (node.left.val > high) {
                node.left = node.left.right;// 这里直接舍弃一半真的太舒服了
            } else {
                node = node.left;// 到这里说明 node.left 是满足条件的
            }
        }
        node = root;
        while (node.right != null) {
            if (node.right.val < low) {
                node.right = node.right.right;
            } else if (node.right.val > high) {
                node.right = node.right.left;
            } else {
                node = node.right;
            }
        }

        return root;
    }
}

自己实现过程中遇到哪些困难

    一开始自己写的太乱了, 对于递归的执行顺序也没有想明白. 想了一堆没有用的逻辑. 后来简化完之后, 发现自己之前提交的代码, 可以通过删除代码得到简化的版本. 修建二叉树的逻辑看起来简单, 但是似乎自己并没有掌握得很好.

[LeetCode] 108. 将有序数组转换为二叉搜索树

[LeetCode] 108. 将有序数组转换为二叉搜索树 文章解释

[LeetCode] 108. 将有序数组转换为二叉搜索树 视频解释

题目:

给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵

平衡

二叉搜索树。

示例 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 <= 10^4
  • -10^4 <= nums[i] <= 10^4
  • nums严格递增 顺序排列

[LeetCode] 108. 将有序数组转换为二叉搜索树

自己看到题目的第一想法

    去中间的值生成根节点, 然后往左右两边递归生成左右子树

看完代码随想录之后的想法

    思路是一致的.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 迭代法: 利用三个栈, 分别记录每个节点对应的序列区间范围
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if (nums == null) {
            return null;
        }
        Stack<Integer> leftIndex = new Stack<>();
        Stack<Integer> rightIndex = new Stack<>();
        Stack<TreeNode> nodes = new Stack<>();
        TreeNode node;
        TreeNode root = new TreeNode(0);
        nodes.push(root);
        leftIndex.push(0);
        rightIndex.push(nums.length - 1);
        while (!nodes.isEmpty()) {
            node = nodes.pop();
            int left = leftIndex.pop();
            int right = rightIndex.pop();
            if (left <= right) {
                int mid = left + ((right - left) >> 1);
                node.val = nums[mid];
                if (left <= mid - 1) {
                    node.left = new TreeNode();
                    leftIndex.push(left);
                    rightIndex.push(mid - 1);
                    nodes.push(node.left);
                }
                if (right >= mid + 1) {
                    node.right = new TreeNode();
                    leftIndex.push(mid + 1);
                    rightIndex.push(right);
                    nodes.push(node.right);
                }
            }
        }
        return root;
    }
}

自己实现过程中遇到哪些困难

    迭代法没想出来~ 看了解释以后就简单多了.

[LeetCode] 538. 把二叉搜索树转换为累加树

[LeetCode] 538. 把二叉搜索树转换为累加树 文章解释

[LeetCode] 538. 把二叉搜索树转换为累加树 视频解释

题目:

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

注意:本题和
[LeetCode] 1038 相同(PS: 不是我想换行, 是编辑器强制的... 位了编辑这几个 link, 浪费了我好几分钟. 各种方式都尝试了... 真是无奈.)

示例 1:

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

示例 2:

输入:root = [0,null,1]
输出:[1,null,1]

示例 3:

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

示例 4:

输入:root = [3,2,4,1]
输出:[7,9,4,10]

提示:

  • 树中的节点数介于 0 和 10^4 之间。
  • 每个节点的值介于 -10^4 和 10^4 之间。
  • 树中的所有值 互不相同
  • 给定的树为二叉搜索树。

[LeetCode] 538. 把二叉搜索树转换为累加树 

自己看到题目的第一想法

    用右中左的方式遍历. 递归或者迭代应该都可以.

看完代码随想录之后的想法

     思路是一致的. 但是因为递归结束条件的不同, 我自己实现的版本代码写的有点啰嗦.

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private int preNodeSum = 0;
    public TreeNode convertBST(TreeNode root) {
        if (root == null) {
            return null;
        }
        convertBST(root.right);
        root.val += preNodeSum;
        preNodeSum = root.val;
        convertBST(root.left);
        return root;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    private TreeNode preNode = 0;
    public TreeNode convertBST(TreeNode root) {
        if (root == null) {
            return null;
        }
        convertBST(root.right);
        root.val += (preNodeSum == null ? 0: preNode.val);
        preNode = root;
        convertBST(root.left);
        return root;
    }
}
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
// 迭代法: 这一题用迭代法还是比较清爽和简单的. 套模板就可以了.
class Solution {
    private TreeNode preNode = null;
    public TreeNode convertBST(TreeNode root) {
        Stack<TreeNode> nodes = new Stack<>();
        TreeNode node = root;
        while (node != null || !nodes.isEmpty()) {
            if (node != null) {
                nodes.push(node);
                node = node.right;
            } else {
                node = nodes.pop();
                if (preNode != null) {
                    node.val += preNode.val;
                }
                preNode = node;
                node = node.left;
            }
        }

        return root;
    }
}

自己实现过程中遇到哪些困难

    递归结束条件写复杂了, 导致代码很冗余. 看了视频后有种豁然开朗的感觉, 原来只要一般的代码量就够了呀!

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
第二十二算法训练营主要涵盖了Leetcode题目中的三道题目,分别是Leetcode 28 "Find the Index of the First Occurrence in a String",Leetcode 977 "有序数组的平方",和Leetcode 209 "长度最小的子数组"。 首先是Leetcode 28题,题目要求在给定的字符串中找到第一个出现的字符的索引。思路是使用双指针来遍历字符串,一个指向字符串的开头,另一个指向字符串的结尾。通过比较两个指针所指向的字符是否相等来判断是否找到了第一个出现的字符。具体实现的代码如下: ```python def findIndex(self, s: str) -> int: left = 0 right = len(s) - 1 while left <= right: if s[left == s[right]: return left left += 1 right -= 1 return -1 ``` 接下来是Leetcode 977题,题目要求对给定的有序数组中的元素进行平方,并按照非递减的顺序返回结果。这里由于数组已经是有序的,所以可以使用双指针的方法来解决问题。一个指针指向数组的开头,另一个指针指向数组的末尾。通过比较两个指针所指向的元素的绝对值的大小来确定哪个元素的平方应该放在结果数组的末尾。具体实现的代码如下: ```python def sortedSquares(self, nums: List[int]) -> List[int]: left = 0 right = len(nums) - 1 ans = [] while left <= right: if abs(nums[left]) >= abs(nums[right]): ans.append(nums[left ** 2) left += 1 else: ans.append(nums[right ** 2) right -= 1 return ans[::-1] ``` 最后是Leetcode 209题,题目要求在给定的数组中找到长度最小的子数组,使得子数组的和大于等于给定的目标值。这里可以使用滑动窗口的方法来解决问题。使用两个指针来表示滑动窗口的左边界和右边界,通过移动指针来调整滑动窗口的大小,使得滑动窗口中的元素的和满足题目要求。具体实现的代码如下: ```python def minSubArrayLen(self, target: int, nums: List[int]) -> int: left = 0 right = 0 ans = float('inf') total = 0 while right < len(nums): total += nums[right] while total >= target: ans = min(ans, right - left + 1) total -= nums[left] left += 1 right += 1 return ans if ans != float('inf') else 0 ``` 以上就是第二十二算法训练营的内容。通过这些题目的练习,可以提升对双指针和滑动窗口等算法的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值