1. 修剪二叉搜索树(LeetCode 669)
1.1 问题理解与递归解法
这道题要求我们修剪二叉搜索树(BST),使其所有节点值都在给定范围[low, high]内。BST的特性是左子树节点值小于根节点,右子树节点值大于根节点。这个特性决定了我们的修剪策略:
当遇到节点值小于low时,其左子树所有节点必然都小于low,但右子树可能存在有效节点。因此我们只需保留右子树:
python复制if root.val < low:
return trimBST(root.right, low, high)
同理,当节点值大于high时,保留左子树:
python复制if root.val > high:
return trimBST(root.left, low, high)
对于在范围内的节点,我们需要递归处理其左右子树:
python复制root.left = trimBST(root.left, low, high)
root.right = trimBST(root.right, low, high)
1.2 实现细节与注意事项
完整Python实现:
python复制def trimBST(root, low, high):
if not root:
return None
if root.val < low:
return trimBST(root.right, low, high)
if root.val > high:
return trimBST(root.left, low, high)
root.left = trimBST(root.left, low, high)
root.right = trimBST(root.right, low, high)
return root
关键注意事项:
- 递归终止条件:遇到空节点直接返回None
- 返回值处理:必须用root.left/right接收递归返回值,这样才能正确更新树结构
- 时间复杂度:O(N),最坏情况需要访问所有节点
提示:在实际面试中,可以先用一个简单例子(如[1,0,2]修剪为[1,2])手动演示递归过程,这能帮助面试官理解你的思路。
2. 有序数组转平衡BST(LeetCode 108)
2.1 分治策略与平衡保证
将有序数组转换为平衡BST的关键在于:
- 始终保持左右子树节点数量平衡
- 利用数组已排序的特性
最佳策略是每次都选择中间元素作为根节点:
python复制mid = (left + right) // 2
root = TreeNode(nums[mid])
root.left = build(nums, left, mid-1)
root.right = build(nums, mid+1, right)
2.2 实现细节与边界处理
完整Python实现:
python复制def sortedArrayToBST(nums):
def build(left, right):
if left > right:
return None
mid = (left + right) // 2
root = TreeNode(nums[mid])
root.left = build(left, mid-1)
root.right = build(mid+1, right)
return root
return build(0, len(nums)-1)
关键点:
- 边界条件:当left > right时返回None
- 中间值选择:对于偶数长度数组,选靠左或靠右的中间值都可以保证平衡
- 时间复杂度:O(N),每个元素只被访问一次
注意:虽然题目不要求处理空输入,但实际工程中应该检查nums是否为空,避免索引错误。
3. BST转累加树(LeetCode 538)
3.1 反序中序遍历策略
累加树的定义是每个节点的值等于原树中所有大于等于它的节点值之和。利用BST的性质,我们可以:
- 采用"右-根-左"的反序中序遍历
- 维护一个累加变量
- 遍历时不断更新节点值
python复制self.total = 0
def traverse(node):
if not node:
return
traverse(node.right)
self.total += node.val
node.val = self.total
traverse(node.left)
3.2 实现变体与注意事项
不使用全局变量的实现:
python复制def convertBST(root):
total = 0
def traverse(node):
nonlocal total
if node:
traverse(node.right)
total += node.val
node.val = total
traverse(node.left)
traverse(root)
return root
关键细节:
- 遍历顺序必须是右子树优先
- 累加变量需要在递归调用间保持状态
- 可以改用迭代法实现,使用显式栈:
python复制def convertBST(root):
total = 0
stack = []
node = root
while stack or node:
while node:
stack.append(node)
node = node.right
node = stack.pop()
total += node.val
node.val = total
node = node.left
return root
4. 二叉树问题解题方法论
4.1 递归三要素
解决二叉树问题的递归方法需要明确:
- 终止条件:通常是遇到空节点
- 当前层逻辑:对当前节点的处理
- 递归调用:如何处理子问题
4.2 遍历方式选择
根据问题特点选择遍历顺序:
- 前序:根-左-右(适合自顶向下处理)
- 中序:左-根-右(BST相关问题时常用)
- 后序:左-右-根(适合自底向上处理)
- 反序中序:右-根-左(如累加树问题)
4.3 空间复杂度优化
递归解法隐式使用调用栈,空间复杂度O(H)(树高)。对于特别深的树,可以考虑:
- 迭代法(显式使用栈)
- Morris遍历(利用空闲指针,空间O(1))
5. 常见错误与调试技巧
5.1 指针丢失问题
在修剪BST时,容易忘记用root.left/right接收递归返回值,导致树结构断裂。调试时可以:
- 打印每个递归调用的输入输出
- 使用可视化工具观察树结构变化
5.2 边界条件处理
特别注意:
- 空输入处理
- 单节点树
- 完全左倾/右倾的树
- 重复元素处理(如果题目允许)
5.3 递归调试技巧
- 添加深度参数打印递归树:
python复制def traverse(node, depth=0):
print(" "*depth + f"Entering {node.val if node else None}")
# ...递归调用时传入depth+1
- 使用小例子手动模拟递归过程
6. 进阶练习建议
掌握这三道题后,可以尝试:
- 迭代法实现所有题目
- 相关变种题:
-
- 删除BST中的节点
-
- BST中的插入操作
-
- BST中第K小的元素
-
- 应用这些技术解决实际问题,如数据库索引优化、文件系统设计等
在实际工程中,BST的变种(如AVL树、红黑树)被广泛用于实现高效的数据存储和检索。理解这些基础算法能为学习更复杂的数据结构打下坚实基础。