修剪二叉搜索树

标签: 深度优先搜索 二叉搜索树 二叉树

难度: Medium

给你二叉搜索树的根节点 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, 104]
  • 0 <= Node.val <= 104
  • 树中每个节点的值都是 唯一
  • 题目数据保证输入是一棵有效的二叉搜索树
  • 0 <= low <= high <= 104

Submission

运行时间: 60 ms

内存: 19.2 MB

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
        if root is None:
            return
        if root.val < low:
            return self.trimBST(root.right, low, high)
        if root.val > high:
            return self.trimBST(root.left, low, high)
        root.left = self.trimBST(root.left, low, high)
        root.right = self.trimBST(root.right, low, high)
        return root
 

Explain

这个题解使用递归的方式对二叉搜索树进行修剪。具体思路如下: 1. 如果当前节点为空,直接返回。 2. 如果当前节点的值小于下限 low,说明该节点及其左子树都应该被剪掉,返回对右子树修剪的结果。 3. 如果当前节点的值大于上限 high,说明该节点及其右子树都应该被剪掉,返回对左子树修剪的结果。 4. 如果当前节点的值在 [low, high] 范围内,则递归地对左子树和右子树进行修剪,并返回修剪后的当前节点。

时间复杂度: O(n)

空间复杂度: O(n)

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
        # 如果当前节点为空,直接返回
        if root is None:
            return
        
        # 如果当前节点的值小于下限 low,返回对右子树修剪的结果
        if root.val < low:
            return self.trimBST(root.right, low, high)
        
        # 如果当前节点的值大于上限 high,返回对左子树修剪的结果
        if root.val > high:
            return self.trimBST(root.left, low, high)
        
        # 递归地对左子树进行修剪
        root.left = self.trimBST(root.left, low, high)
        # 递归地对右子树进行修剪 
        root.right = self.trimBST(root.right, low, high)
        
        # 返回修剪后的当前节点
        return root

Explore

在二叉搜索树中,任何节点的左子树中的所有节点的值都小于该节点的值。因此,如果一个节点的值小于`low`,那么它的左子树中的所有节点的值也必然小于`low`。这意味着左子树的所有节点都不在指定的范围[low, high]内,因此可以被完全剪掉而不用递归修剪。我们只需要递归处理右子树,因为右子树中可能存在一些节点的值位于这个范围内。

是的,这正是这种处理方式的含义。在二叉搜索树中,任何节点的右子树中的所有节点的值都大于该节点的值。因此,如果一个节点的值大于`high`,那么它的右子树中的所有节点的值也必然大于`high`。这意味着右子树的所有节点都不在指定的范围[low, high]内,可以被完全剪掉,只需要递归修剪左子树,因为左子树中可能存在一些节点的值位于这个范围内。

这种操作是安全的并且是必要的,因为我们的目标是调整树中节点的链接以保持二叉搜索树的条件,并且只包含在[low, high]范围内的节点。修剪左子树和右子树后,我们得到的新的左右子树已经是修剪过的,即它们的所有节点都在指定范围内。通过将这些修剪过的子树重新链接到当前的根节点,我们确保了整个树依然保持二叉搜索树的结构。这不会造成节点丢失,而是正是去除不在范围内的节点的必要步骤。

是的,题解中的逻辑确保了修剪后的树仍然满足二叉搜索树的所有性质。对于处在范围[low, high]内的节点,题解中通过递归修剪其左右子树并链接回这些节点,确保了所有子树都只包含在范围内的节点。由于这个过程不改变节点间的相对大小关系,修剪后的树依然保持了二叉搜索树的性质,即任何节点的左子树包含的节点的值都小于该节点的值,右子树包含的节点的值都大于该节点的值。