1. 二叉树与二叉搜索树核心算法全解析
作为数据结构中最基础也最重要的非线性存储结构,树形结构在实际开发中无处不在。今天我想系统梳理二叉树特别是二叉搜索树(BST)相关的十大高频算法问题,这些都是大厂面试的必考内容,也是日常开发中经常遇到的场景。
我从业十年来处理过无数树形结构问题,发现90%的树相关问题都可以拆解为遍历+递归的组合解法。本文将用真实代码示例演示如何用统一思维解决合并二叉树、BST搜索验证、节点操作等经典问题,并分享我在处理海量树形数据时总结的性能优化技巧。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础概念与遍历框架
2.1 二叉树与BST的区别
普通二叉树每个节点最多有两个子节点,没有任何数值约束。而二叉搜索树在二叉树基础上增加了以下约束:
- 左子树所有节点值 < 根节点值
- 右子树所有节点值 > 根节点值
- 左右子树也必须是BST
这种结构使得BST的中序遍历结果必然是有序序列,这是许多BST算法的基础特性。
2.2 递归遍历模板
所有树问题都基于三种遍历方式(前序、中序、后序),这里给出万能递归模板:
python复制def traverse(root):
if not root:
return
# 前序位置
traverse(root.left)
# 中序位置
traverse(root.right)
# 后序位置
提示:BST问题大部分在中序位置处理,因为能获取有序序列
3. 高频算法问题详解
3.1 合并二叉树(LeetCode 617)
问题:将两棵二叉树合并,重叠节点值相加。
python复制def mergeTrees(t1, t2):
if not t1:
return t2
if not t2:
return t1
t1.val += t2.val
t1.left = mergeTrees(t1.left, t2.left)
t1.right = mergeTrees(t1.right, t2.right)
return t1
避坑指南:注意处理节点为None的情况,这是递归终止条件
3.2 验证二叉搜索树(LeetCode 98)
利用BST中序有序的特性:
python复制def isValidBST(root):
prev = float('-inf')
def inorder(node):
nonlocal prev
if not node:
return True
if not inorder(node.left):
return False
if node.val <= prev:
return False
prev = node.val
return inorder(node.right)
return inorder(root)
3.3 BST的最小绝对差(LeetCode 530)
中序遍历时记录相邻节点差值:
python复制def getMinimumDifference(root):
min_diff = float('inf')
prev = None
def inorder(node):
nonlocal min_diff, prev
if not node:
return
inorder(node.left)
if prev is not None:
min_diff = min(min_diff, node.val - prev)
prev = node.val
inorder(node.right)
inorder(root)
return min_diff
3.4 BST的众数(LeetCode 501)
统计出现频率最高的值:
python复制def findMode(root):
count = {}
max_count = 0
result = []
def inorder(node):
nonlocal max_count
if not node:
return
inorder(node.left)
count[node.val] = count.get(node.val, 0) + 1
if count[node.val] > max_count:
max_count = count[node.val]
result = [node.val]
elif count[node.val] == max_count:
result.append(node.val)
inorder(node.right)
inorder(root)
return result
4. 最近公共祖先问题
4.1 二叉树的LCA(LeetCode 236)
经典递归解法:
python复制def lowestCommonAncestor(root, p, q):
if not root or root == p or root == q:
return root
left = lowestCommonAncestor(root.left, p, q)
right = lowestCommonAncestor(root.right, p, q)
if left and right:
return root
return left if left else right
4.2 BST的LCA(LeetCode 235)
利用BST特性优化:
python复制def lowestCommonAncestor(root, p, q):
while root:
if root.val > p.val and root.val > q.val:
root = root.left
elif root.val < p.val and root.val < q.val:
root = root.right
else:
return root
return None
5. BST的增删改操作
5.1 插入节点(LeetCode 701)
python复制def insertIntoBST(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insertIntoBST(root.left, val)
else:
root.right = insertIntoBST(root.right, val)
return root
5.2 删除节点(LeetCode 450)
最复杂的BST操作,分三种情况处理:
python复制def deleteNode(root, key):
if not root:
return None
if key < root.val:
root.left = deleteNode(root.left, key)
elif key > root.val:
root.right = deleteNode(root.right, key)
else:
if not root.left:
return root.right
if not root.right:
return root.left
min_node = findMin(root.right)
root.val = min_node.val
root.right = deleteNode(root.right, min_node.val)
return root
def findMin(node):
while node.left:
node = node.left
return node
5.3 修剪BST(LeetCode 669)
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
6. 性能优化与工程实践
在处理大规模树数据时,我总结了几点经验:
- 迭代法替代递归:对于深度很大的树,递归可能导致栈溢出
python复制# 以中序遍历为例
def inorderTraversal(root):
stack, res = [], []
curr = root
while curr or stack:
while curr:
stack.append(curr)
curr = curr.left
curr = stack.pop()
res.append(curr.val)
curr = curr.right
return res
- 莫里斯遍历:优化空间复杂度到O(1)
python复制def inorderMorris(root):
curr = root
res = []
while curr:
if not curr.left:
res.append(curr.val)
curr = curr.right
else:
pre = curr.left
while pre.right and pre.right != curr:
pre = pre.right
if not pre.right:
pre.right = curr
curr = curr.left
else:
pre.right = None
res.append(curr.val)
curr = curr.right
return res
- 批量操作优化:当需要频繁插入删除时,可以考虑转为有序数组处理后再重建BST
7. 常见问题排查
- 为什么我的BST验证总是失败?
- 检查是否正确处理了等于的情况(BST通常不允许重复值)
- 确保没有错误地修改了树结构
- 删除节点后树不满足BST性质?
- 确保在删除时正确处理了左右子树都存在的情况
- 替换节点值后要递归删除被替换的节点
- LCA算法返回错误结果?
- 检查是否处理了节点本身就是LCA的情况
- 对于BST版本,确认是否正确利用了大小关系
- 递归解法栈溢出?
- 转换为迭代实现
- 检查树是否退化成链表(此时深度=节点数)
