1. 二叉树基础与递归遍历
二叉树作为数据结构中的常青树,在算法面试中出现的频率高达70%以上。这次训练营的Day14聚焦二叉树基础与递归遍历,正是打牢算法根基的关键一步。我们先从二叉树的链式存储结构说起:
cpp复制struct TreeNode {
int val;
TreeNode *left;
TreeNode *right;
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
};
这种每个节点包含左右指针的结构,使得二叉树天生适合用递归处理。递归遍历的三大经典写法——前序、中序、后序,区别仅在于访问根节点的时机:
python复制# 前序遍历:根->左->右
def preorder(root):
if not root: return
print(root.val) # 访问根
preorder(root.left) # 左子树
preorder(root.right) # 右子树
# 中序遍历:左->根->右
def inorder(root):
if not root: return
inorder(root.left) # 左子树
print(root.val) # 访问根
inorder(root.right) # 右子树
# 后序遍历:左->右->根
def postorder(root):
if not root: return
postorder(root.left) # 左子树
postorder(root.right) # 右子树
print(root.val) # 访问根
关键理解:递归遍历本质上是深度优先搜索(DFS),每次递归调用都会在调用栈中压入新的上下文。对于n个节点的二叉树,递归遍历的时间复杂度为O(n),空间复杂度在最坏情况下(树退化为链表)也是O(n)。
实际编码时容易忽略递归终止条件。我曾在一个二叉搜索树项目中忘记判断if not root,导致在空子树情况下访问root.val引发段错误。正确的做法应该像上面代码那样,先检查节点是否为空。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 迭代法实现遍历的两种范式
虽然递归写法简洁,但面试中常要求用迭代实现。这就需要借助栈来模拟递归的调用过程。迭代法主要有两种实现范式:
2.1 标记法(统一写法)
这是我最推荐的写法,通过引入空标记节点来统一三种遍历的逻辑:
python复制def preorderTraversal(root):
result = []
stack = [root] if root else []
while stack:
node = stack.pop()
if node is None: # 遇到标记节点
node = stack.pop()
result.append(node.val)
continue
# 调整下面三行的顺序即可实现不同遍历
if node.right: stack.append(node.right) # 右
if node.left: stack.append(node.left) # 左
stack.append(node) # 根
stack.append(None) # 标记
return result
这种写法的优势在于:
- 只需调整左右根入栈顺序,就能切换遍历方式
- 避免了传统写法中各种
if-else分支判断 - 代码结构统一,便于记忆和调试
2.2 传统迭代法
以前序遍历为例,更符合直觉的写法是:
python复制def preorderTraversal(root):
res = []
stack = []
curr = root
while curr or stack:
while curr: # 深入左子树
res.append(curr.val) # 访问
sta
