目录
669. 修剪二叉搜索树
这道题目比较难,比 添加增加和删除节点难的多,建议先看视频理解。
题目链接/文章讲解: 代码随想录
视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树_哔哩哔哩_bilibili
思路梳理(抓住 BST 性质)
设区间是
[low, high](闭区间,包含边界):
若
root.val < low:
root以及它的整棵左子树都比low小,不可能保留;
只可能在 右子树里找到可保留的节点 ⇒ 直接返回trim(root.right)。若
root.val > high:
root以及它的整棵右子树都比high大,也不可能保留;
只可能在 左子树里找到可保留的节点 ⇒ 直接返回trim(root.left)。若
low ≤ root.val ≤ high:
root可以保留,但左右子树仍需分别修剪:
root.left = trim(root.left),root.right = trim(root.right),最后返回root。时间复杂度:
O(n)(每个节点最多访问一次)
空间复杂度:递归O(h),迭代O(1)(h为树高,平衡树约log n,最坏n)
解法一:递归
root.val < low⇒ 左子树所有值< root.val < low,全都不合格,只去右边找。
root.val > high⇒ 右子树所有值> root.val > high,全都不合格,只去左边找。
# 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: 'TreeNode', low: int, high: int) -> 'TreeNode':
"""
递归修剪:
- 利用 BST 性质判断当前节点与区间 [low, high] 的关系,
决定“整棵丢弃/保留并继续修剪哪一边/两边”。
"""
if root is None:
return None
# 情况1:当前值过小 -> 当前节点及其左子树都不可能在区间内,去右子树找
if root.val < low:
return self.trimBST(root.right, low, high)
# 情况2:当前值过大 -> 当前节点及其右子树都不可能在区间内,去左子树找
if root.val > high:
return self.trimBST(root.left, low, high)
# 情况3:当前值在区间内 -> 当前节点保留,但左右子树需要继续修剪
root.left = self.trimBST(root.left, low, high)
root.right = self.trimBST(root.right, low, high)
return root
解法二:迭代(不占递归栈,更贴工程)
迭代思路三步走(利用指针“往下走并就地修剪”):
把根挪进区间:
若root.val < low,根换成root.right;若root.val > high,根换成root.left;直到根在区间内或树为空。修剪左侧(去掉所有
当发现< low的左链节点):
从根一路向左:cur.left存在且cur.left.val < low,就把cur.left替换为cur.left.right(因为cur.left以及它的整棵左子树都小于low,只有它的右子树还有可能落在区间内)。修剪右侧(去掉所有
当发现> high的右链节点):
从根一路向右:cur.right存在且cur.right.val > high,就把cur.right替换为cur.right.left(对称地,cur.right及其右子树都过大,只保留它的左子树)。
# 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: 'TreeNode', low: int, high: int) -> 'TreeNode':
"""
迭代修剪:
1) 先把 root 调整到区间内
2) 修剪左侧:移除所有 < low 的左孩子(用其右孩子顶替)
3) 修剪右侧:移除所有 > high 的右孩子(用其左孩子顶替)
"""
# 1) 根不在区间内时,不断向下“跳过”整侧子树,直到根在区间内或为空
while root and (root.val < low or root.val > high):
if root.val < low:
root = root.right # 左边整棵都太小,跳到右边
else: # root.val > high
root = root.left # 右边整棵都太大,跳到左边
if not root:
return None # 整棵树都不在区间内
# 2) 修剪左侧:消除所有 < low 的左孩子
cur = root
while cur:
# 当左孩子存在且过小时,用“左孩子的右子树”来顶替它
while cur.left and cur.left.val < low:
cur.left = cur.left.right
cur = cur.left # 继续沿着左链向下检查
# 3) 修剪右侧:消除所有 > high 的右孩子
cur = root
while cur:
# 当右孩子存在且过大时,用“右孩子的左子树”来顶替它
while cur.right and cur.right.val > high:
cur.right = cur.right.left
cur = cur.right # 继续沿着右链向下检查
return root
108.将有序数组转换为二叉搜索树
本题就简单一些,可以尝试先自己做做。
视频讲解:构造平衡二叉搜索树!| LeetCode:108.将有序数组转换为二叉搜索树_哔哩哔哩_bilibili
思路梳理(简明)
输入是递增有序数组
nums。选中间位置
mid作为根:root = TreeNode(nums[mid])。左半段
[l, mid-1]递归建左子树,右半段[mid+1, r]递归建右子树。当
l > r时子树为空,返回None。中点选择:
mid = (l + r) // 2(或(l + r + 1) // 2都行,差别很小)。为什么“近似平衡”:每层都把区间对半切,因此树高接近
log n。
复杂度:时间O(n)(每个元素建一次节点),空间O(log 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 sortedArrayToBST(self, nums: list[int]) -> 'TreeNode':
"""
思路:分治 + 递归
- 取中点为根,左边建左子树,右边建右子树
- 区间为空时返回 None
- 因为每次都尽量对半拆,得到的 BST 近似平衡
"""
def build(l: int, r: int) -> 'TreeNode | None':
# 区间为空 -> 空子树
if l > r:
return None
# 选中点为根(取左中点/右中点都可以)
mid = (l + r) >> 1 # 等价于 (l + r) // 2
root = TreeNode(nums[mid])
# 递归构建左右子树
root.left = build(l, mid - 1)
root.right = build(mid + 1, r)
return root
return build(0, len(nums) - 1)
538.把二叉搜索树转换为累加
本题也不难,在 求二叉搜索树的最小绝对差 和 众数 那两道题目 都讲过了 双指针法,思路是一样的。
视频讲解:普大喜奔!二叉树章节已全部更完啦!| LeetCode:538.把二叉搜索树转换为累加树_哔哩哔哩_bilibili
给你一棵 BST,让每个节点的新值 = 它本身的值 + 所有比它大的节点的值。
4
/ \
1 6
中序序列是 [1,4,6]
-
1 → 新值 = 1 + 4 + 6 = 11
-
4 → 新值 = 4 + 6 = 10
-
6 → 新值 = 6
核心思路:反向中序遍历
BST 的中序(Left → Node → Right)是递增序列。
所以我们只要 反向遍历(Right → Node → Left),就是从大到小访问。
👉 遍历时维护一个累计和 s:
-
一开始
s = 0 -
每访问一个节点,就做:
-
s += 节点原值 -
节点新值 = s
-
这样,s 就一直保存了“所有已经访问过的更大节点之和”。
步骤举例
以 BST:
4
/ \
1 6
/ \
5 7
中序序列(升序): [1,4,5,6,7]
反向中序序列(降序): [7,6,5,4,1]
遍历过程
-
访问 7:
s = 0 + 7 = 7
节点 7 → 新值 = 7 -
访问 6:
s = 7 + 6 = 13
节点 6 → 新值 = 13 -
访问 5:
s = 13 + 5 = 18
节点 5 → 新值 = 18 -
访问 4:
s = 18 + 4 = 22
节点 4 → 新值 = 22 -
访问 1:
s = 22 + 1 = 23
节点 1 → 新值 = 23
结果树
22
/ \
23 13
/ \
18 7
解法一:递归反向中序(直观,推荐)
# 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 convertBST(self, root: 'TreeNode') -> 'TreeNode':
"""
反向中序(Right -> Node -> Left)遍历 BST:
- 维护一个累计和 self.s,表示“已经处理过的更大节点值之和”
- 降序访问,先到最右(最大),一路向左推进
- 每到一个节点:先把它的值加到累计和,再用累计和覆盖该节点
"""
self.s = 0 # 累计和(到当前为止,已访问过的更大或相等节点之和)
def dfs(node: 'TreeNode'):
if not node:
return
# 1) 先处理右子树(更大的值)
dfs(node.right)
# 2) 再处理当前节点:把当前值纳入累计和,并回填到节点
self.s += node.val
node.val = self.s
# 3) 最后处理左子树(较小的值)
dfs(node.left)
dfs(root)
return root
解法二:迭代(显式栈,避免递归栈)
# 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 convertBST(self, root: 'TreeNode') -> 'TreeNode':
"""
迭代版反向中序:
- 用显式栈模拟递归
- 模板:一路向右入栈 -> 弹栈访问节点 -> 转向左子树
"""
s = 0
stack = []
cur = root
# 反向中序模板:优先向右深入
while cur or stack:
# 1) 沿右链入栈(降序中更靠前)
while cur:
stack.append(cur)
cur = cur.right
# 2) 弹出一个节点 = 访问该节点
cur = stack.pop()
# 用累计和更新当前节点
s += cur.val
cur.val = s
# 3) 转向左子树
cur = cur.left
return root
总结篇
好了,二叉树大家就这样刷完了,做一个总结吧
|第六章 二叉树part08|669. 修剪二叉搜索树`108.将有序数组转换为二叉搜索树`538.把二叉搜索树转换为累加&spm=1001.2101.3001.5002&articleId=150554019&d=1&t=3&u=90fa0e4e8e714920807b3c8cae0c7a97)
220

被折叠的 条评论
为什么被折叠?



