1. 二叉树核心要点复盘
1.1 二叉树遍历的三种姿势
前序遍历(根-左-右)、中序遍历(左-根-右)、后序遍历(左-右-根)是二叉树最基础的三种遍历方式。在实际编码中,递归实现最为直观:
python复制# 前序遍历递归实现
def preorder(root):
if not root:
return
print(root.val) # 访问根节点
preorder(root.left) # 递归左子树
preorder(root.right) # 递归右子树
但递归存在栈溢出风险,对于深度较大的树,建议使用迭代法配合栈结构实现。以中序遍历为例:
python复制def inorder(root):
stack = []
while stack or root:
while root: # 将左边界全部压栈
stack.append(root)
root = root.left
node = stack.pop() # 弹出最左侧节点
print(node.val) # 访问节点
root = node.right # 转向右子树
关键技巧:迭代法中序遍历时,栈顶元素永远是最左侧未被访问的节点,这个性质在BST验证等场景非常有用
1.2 二叉树深度与高度计算
二叉树的最大深度(高度)计算是高频考点,递归解法非常简洁:
python复制def maxDepth(root):
if not root:
return 0
return 1 + max(maxDepth(root.left), maxDepth(root.right))
但实际工程中更推荐层序遍历(BFS)的实现方式,既能计算深度,又能保留层级信息:
python复制from collections import deque
def maxDepth(root):
if not root:
return 0
queue = deque([root])
depth = 0
while queue:
depth += 1
for _ in range(len(queue)): # 处理当前层所有节点
node = queue.popleft()
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
1.3 二叉树子树问题
判断子树结构是二叉树中的经典问题,通常需要结合递归和遍历:
python复制def isSubtree(root, subRoot):
def isSameTree(p, q):
if not p and not q:
return True
if not p or not q:
return False
return p.val == q.val and isSameTree(p.left, q.left) and isSameTree(p.right, q.right)
if not root:
return False
return isSameTree(root, subRoot) or isSubtree(root.left, subRoot) or isSubtree(root.right, subRoot)
时间复杂度分析:最坏情况下(树退化为链表)为O(mn),其中m和n分别是两棵树的节点数。优化方向可以考虑序列化+字符串匹配(KMP算法)。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 栈与队列实战技巧
2.1 栈的经典应用场景
栈在算法中主要有三大应用方向:
- 括号匹配:使用栈检查表达式合法性
- 表达式求值:中缀转后缀表达式
- 递归模拟:将递归调用栈显式化
以括号匹配为例的典型实现:
python复制def isValid(s: str) -> bool:
stack = []
mapping = {')': '(', ']': '[', '}': '{'}
for char in s:
if char in mapping: # 遇到右括号
top = stack.pop() if stack else '#'
if mapping[char] != top:
return False
else: # 遇到左括号
stack.append(char)
return not stack
2.2 队列的进阶用法
除了基础的FIFO特性,队列在算法中还有以下重要变体:
- 双端队列(Deque):两端都可操作的队列
- 优先队列(PriorityQueue):按优先级出队
- 单调队列:维护队列元素的单调性
单调队列在滑动窗口最大值问题中表现优异:
python复制from collections import deque
def maxSlidingWindow(nums, k):
q = deque()
res = []
for i, num in enumerate(nums):
while q and nums[q[-1]] <= num: # 维护单调递减
q.pop()
q.append(i)
if q[0] == i - k: # 移除超出窗口的元素
q.popleft()
if i >= k - 1:
res.append(nums[q[0]])
return res
时间复杂度从暴力解的O(nk)优化到O(n),空间复杂度O(k)。
2.3 递归与栈的转换
所有递归都可以用栈+循环改写,以斐波那契数列为例:
递归版本(O(2^n)时间复杂度):
python复制def fib(n):
if n <= 1:
return n
return fib(n-1) + fib(n-2)
栈模拟版本(O(n)时间复杂度):
python复制def fib(n):
if n <= 1:
return n
stack = [(n, False)]
res = 0
while stack:
num, visited = stack.pop()
if num <= 1:
res += num
else:
if visited:
continue
stack.append((num, True))
stack.append((num-1, False))
stack.append((num-2, False))
return res
实际工程中更推荐记忆化递归或动态规划实现,这里仅作递归转栈的示例
3. 高频问题深度解析
3.1 二叉树最近公共祖先(LCA)
LCA问题是二叉树算法中的经典难题,递归解法非常巧妙:
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: # p和q分布在两侧
return root
return left or right # 返回非空的一侧
时间复杂度O(n),空间复杂度O(h)(h为树高)。对于频繁查询的场景,可以考虑预处理构建父指针哈希表,将查询时间优化到O(1)。
3.2 二叉搜索树验证
验证BST的有效性需要注意边界条件:
python复制def isValidBST(root):
def helper(node, lower=float('-inf'), upper=float('inf')):
if not node:
return True
val = node.val
if val <= lower or val >= upper:
return False
return helper(node.left, lower, val) and helper(node.right, val, upper)
return helper(root)
常见错误是只比较当前节点与左右子节点的值,而忽略了整个子树的上下界约束。中序遍历解法同样有效:
python复制def isValidBST(root):
stack, prev = [], float('-inf')
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
if root.val <= prev:
return False
prev = root.val
root = root.right
return True
3.3 栈与队列的相互实现
用栈实现队列需要两个栈(输入栈和输出栈):
python复制class MyQueue:
def __init__(self):
self.in_stack = []
self.out_stack = []
def push(self, x):
self.in_stack.append(x)
def pop(self):
if not self.out_stack:
while self.in_stack:
self.out_stack.append(self.in_stack.pop())
return self.out_stack.pop()
def peek(self):
if not self.out_stack:
while self.in_stack:
self.out_stack.append(self.in_stack.pop())
return self.out_stack[-1]
def empty(self):
return not self.in_stack and not self.out_stack
用队列实现栈则更简单,只需要一个队列:
python复制from collections import deque
class MyStack:
def __init__(self):
self.queue = deque()
def push(self, x):
self.queue.append(x)
for _ in range(len(self.queue) - 1):
self.queue.append(self.queue.popleft())
def pop(self):
return self.queue.popleft()
def top(self):
return self.queue[0]
def empty(self):
return not self.queue
4. 实战问题排查指南
4.1 二叉树遍历常见错误
- 迭代遍历时忘记处理空树情况
- 中序遍历时混淆左右子树压栈顺序
- 后序遍历迭代法缺少访问标记
- 层序遍历时未正确处理层级分隔
4.2 栈溢出问题解决方案
递归深度过大导致的栈溢出可以通过:
- 改为迭代实现
- 使用尾递归优化(Python不支持)
- 增大递归深度限制(不推荐)
- 使用备忘录减少重复计算
4.3 队列边界条件处理
队列操作时需要特别注意:
- 出队前检查队列是否为空
- 循环队列判断满/空条件
- 优先队列的优先级定义
- 阻塞队列的超时处理
4.4 性能优化检查清单
- 二叉树问题是否可以利用BST特性优化
- 栈操作是否可以批量处理减少push/pop次数
- 队列实现是否避免了不必要的元素移动
- 递归是否可以用记忆化优化重复子问题
