二叉树基础
什么是二叉树?
二叉树(Binary Tree)是一种树形数据结构,每个节点最多有两个子节点,分别称为左子节点和右子节点。
二叉树的定义:
- 每个节点最多有 2 个子节点
- 子树有左右之分,不能颠倒
- 第 i 层最多有 2^(i-1) 个节点
- 深度为 k 的二叉树最多有 2^k - 1 个节点
二叉树节点定义:
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
二叉树的性质
-
节点数与深度的关系:
- 第 i 层最多有 2^(i-1) 个节点(i ≥ 1)
- 深度为 k 的二叉树最多有 2^k - 1 个节点
-
叶子节点数:
- n0 = n2 + 1(n0 为叶子节点数,n2 为度为 2 的节点数)
-
完全二叉树的性质:
- 具有 n 个节点的完全二叉树的深度为 ⌊log₂n⌋ + 1
- 对于节点 i,其父节点为 ⌊i/2⌋,左子节点为 2i,右子节点为 2i+1
二叉树的类型
1. 满二叉树(Full Binary Tree)
满二叉树是每个节点都有 0 或 2 个子节点的二叉树。
特点:
- 所有非叶子节点都有 2 个子节点
- 叶子节点都在最后一层
- 有 n 个节点的满二叉树的深度为 log₂(n+1)
2. 完全二叉树(Complete Binary Tree)
完全二叉树是除了最后一层外,其他层的节点都是满的,且最后一层的节点都靠左排列。
特点:
- 适合用数组存储
- 堆(Heap)就是一种完全二叉树
- 最后一个非叶子节点的索引为 ⌊n/2⌋
3. 二叉搜索树(Binary Search Tree, BST)
二叉搜索树是一种有序的二叉树,满足以下性质:
- 左子树所有节点的值 < 根节点的值
- 右子树所有节点的值 > 根节点的值
- 左右子树也都是二叉搜索树
优点:
- 查找、插入、删除的平均时间复杂度为 O(log n)
- 中序遍历可以得到有序序列
缺点:
- 最坏情况下(退化为链表)时间复杂度为 O(n)
4. 平衡二叉树(Balanced Binary Tree)
平衡二叉树是任意节点的左右子树高度差的绝对值不超过 1 的二叉树。
常见实现:
- AVL 树:严格平衡,任意节点左右子树高度差 ≤ 1
- 红黑树:近似平衡,确保最长路径不超过最短路径的 2 倍
5. B+Tree(B+树)
B+Tree 是一种多路平衡搜索树,是 B-Tree 的变体,广泛应用于数据库索引和文件系统中。
B+Tree 的特点:
- 多路平衡树:每个节点可以有多个子节点(通常称为 m 阶 B+Tree)
- 所有数据存储在叶子节点:非叶子节点只存储键值(索引)
- 叶子节点通过指针连接:形成有序链表,方便范围查询
- 所有叶子节点在同一层:保证平衡性
B+Tree 结构示例(3阶):
[10, 20] (非叶子节点,只存储键值)
/ | \
[5, 8] [15, 18] [25, 30] (非叶子节点)
/ | \ / | \ / | \
[1,3,5] [6,7,8] [10,12,15] [16,17,18] [20,22,25] [28,30,35]
(叶子节点,存储数据) (叶子节点通过指针连接)
B+Tree vs B-Tree:
| 特性 | B-Tree | B+Tree |
|---|---|---|
| 数据存储 | 所有节点都可以存储数据 | 只有叶子节点存储数据 |
| 非叶子节点 | 存储键值和数据 | 只存储键值(索引) |
| 叶子节点 | 不连接 | 通过指针连接成链表 |
| 查找效率 | 可能在任何层找到数据 | 必须到叶子节点 |
| 范围查询 | 需要中序遍历 | 通过链表顺序访问 |
| 空间利用率 | 较低 | 较高(非叶子节点更小) |
B+Tree 的优势:
-
适合磁盘存储:
- 节点大小通常等于磁盘页大小(如 4KB)
- 减少磁盘 I/O 次数
- 非叶子节点可以存储更多键值
-
范围查询高效:
- 叶子节点形成有序链表
- 范围查询只需遍历链表,不需要回溯
-
查询稳定:
- 所有查询都要到叶子节点
- 查询路径长度相同,性能稳定
B+Tree 的阶数(m):
- m 阶 B+Tree:每个节点最多有 m 个子节点
- 非叶子节点:最多有 m-1 个键值,至少 ⌈m/2⌉ 个键值
- 叶子节点:最多有 m-1 个数据项,至少 ⌈m/2⌉ 个数据项
B+Tree 操作:
查找:
def search_bplus_tree(root, key):
"""在B+Tree中查找key"""
node = root
# 向下查找直到叶子节点
while not node.is_leaf:
# 在节点中找到合适的子节点
i = 0
while i < len(node.keys) and key >= node.keys[i]:
i += 1
node = node.children[i]
# 在叶子节点中查找
for i, k in enumerate(node.keys):
if k == key:
return node.values[i]
return None # 未找到
插入:
- 找到应该插入的叶子节点
- 如果叶子节点未满,直接插入
- 如果叶子节点已满,进行分裂:
- 将节点分成两部分
- 中间键值提升到父节点
- 如果父节点也满,继续向上分裂
删除:
- 找到包含要删除键值的叶子节点
- 删除键值
- 如果节点键值数量低于最小值,进行合并或借用:
- 尝试从兄弟节点借用
- 如果无法借用,与兄弟节点合并
B+Tree 在数据库中的应用:
MySQL InnoDB 中的 B+Tree:
- 主键索引(聚簇索引):叶子节点存储完整数据行
- 辅助索引(非聚簇索引):叶子节点存储主键值
- 通常使用 3-4 层的 B+Tree 就可以支持千万级数据
索引结构:
聚簇索引(主键索引):
- 叶子节点:存储完整数据行
- 非叶子节点:存储主键值和页指针
辅助索引(二级索引):
- 叶子节点:存储主键值
- 非叶子节点:存储索引键值和页指针
- 需要回表查询(通过主键值查找完整数据)
B+Tree vs 二叉搜索树:
| 特性 | 二叉搜索树 | B+Tree |
|---|---|---|
| 子节点数 | 最多 2 个 | 多个(m 个) |
| 高度 | O(log n) | O(log_m n),更矮 |
| 磁盘 I/O | 多 | 少(节点更大) |
| 范围查询 | 需要中序遍历 | 链表顺序访问 |
| 适用场景 | 内存数据结构 | 磁盘存储、数据库索引 |
扩展知识点:
- B-Tree:B+Tree 的前身,所有节点都可以存储数据
- B*Tree:B+Tree 的变体,非叶子节点之间也有指针连接
- LSM-Tree:另一种数据库索引结构,适合写多读少场景
- 哈希索引:适合等值查询,不支持范围查询
6. 堆(Heap)
堆是一种特殊的完全二叉树,满足堆序性质:
- 大顶堆:父节点的值 ≥ 子节点的值
- 小顶堆:父节点的值 ≤ 子节点的值
7. 线索二叉树(Threaded Binary Tree)
线索二叉树是在二叉链表的基础上,将空指针域利用起来,指向节点的前驱或后继。
二叉树的遍历
1. 前序遍历(Preorder Traversal)
遍历顺序:根节点 → 左子树 → 右子树
递归实现:
def preorder_traversal(root):
result = []
def dfs(node):
if not node:
return
result.append(node.val) # 访问根节点
dfs(node.left) # 遍历左子树
dfs(node.right) # 遍历右子树
dfs(root)
return result
迭代实现:
def preorder_traversal_iterative(root):
if not root:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.val)
# 注意:先压入右节点,再压入左节点
# 因为栈是后进先出,所以左节点会先出栈
if node.right:
stack.append(node.right)
if node.left:
stack.append(node.left)
return result
2. 中序遍历(Inorder Traversal)
遍历顺序:左子树 → 根节点 → 右子树
特点: 对二叉搜索树进行中序遍历,可以得到有序序列。
递归实现:
def inorder_traversal(root):
result = []
def dfs(node):
if not node:
return
dfs(node.left) # 遍历左子树
result.append(node.val) # 访问根节点
dfs(node.right) # 遍历右子树
dfs(root)
return result
迭代实现:
def inorder_traversal_iterative(root):
if not root:
return []
result = []
stack = []
curr = root
while curr or stack:
# 一直向左走到底
while curr:
stack.append(curr)
curr = curr.left
# 弹出并访问
curr = stack.pop()
result.append(curr.val)
# 转向右子树
curr = curr.right
return result
3. 后序遍历(Postorder Traversal)
遍历顺序:左子树 → 右子树 → 根节点
递归实现:
def postorder_traversal(root):
result = []
def dfs(node):
if not node:
return
dfs(node.left) # 遍历左子树
dfs(node.right) # 遍历右子树
result.append(node.val) # 访问根节点
dfs(root)
return result
迭代实现:
def postorder_traversal_iterative(root):
if not root:
return []
result = []
stack = [root]
while stack:
node = stack.pop()
result.append(node.val)
# 先压入左节点,再压入右节点
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
# 反转结果(根->右->左 变成 左->右->根)
return result[::-1]
4. 层序遍历(Level Order Traversal)
层序遍历是按照树的层次,从根节点开始逐层访问节点。
实现:
def level_order_traversal(root):
if not root:
return []
result = []
queue = [root]
while queue:
level = []
size = len(queue)
for _ in range(size):
node = queue.pop(0)
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
使用双端队列优化:
from collections import deque
def level_order_traversal_optimized(root):
if not root:
return []
result = []
queue = deque([root])
while queue:
level = []
size = len(queue)
for _ in range(size):
node = queue.popleft()
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level)
return result
二叉树常见操作
1. 求二叉树的深度
def max_depth(root):
"""递归实现"""
if not root:
return 0
return 1 + max(max_depth(root.left), max_depth(root.right))
def max_depth_iterative(root):
"""迭代实现"""
if not root:
return 0
depth = 0
queue = [root]
while queue:
depth += 1
size = len(queue)
for _ in range(size):
node = queue.pop(0)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return depth
2. 求二叉树的最小深度
def min_depth(root):
if not root:
return 0
# 如果左右子树有一个为空,返回另一个的深度+1
if not root.left:
return 1 + min_depth(root.right)
if not root.right:
return 1 + min_depth(root.left)
# 左右子树都存在,返回较小的深度+1
return 1 + min(min_depth(root.left), min_depth(root.right))
3. 判断是否为相同的二叉树
def is_same_tree(p, q):
if not p and not q:
return True
if not p or not q:
return False
if p.val != q.val:
return False
return is_same_tree(p.left, q.left) and is_same_tree(p.right, q.right)
4. 判断是否为对称二叉树
def is_symmetric(root):
if not root:
return True
def is_mirror(left, right):
if not left and not right:
return True
if not left or not right:
return False
if left.val != right.val:
return False
return is_mirror(left.left, right.right) and is_mirror(left.right, right.left)
return is_mirror(root.left, root.right)
5. 翻转二叉树
def invert_tree(root):
if not root:
return None
# 交换左右子树
root.left, root.right = root.right, root.left
# 递归翻转左右子树
invert_tree(root.left)
invert_tree(root.right)
return root
def invert_tree_iterative(root):
"""迭代实现"""
if not root:
return None
stack = [root]
while stack:
node = stack.pop()
node.left, node.right = node.right, node.left
if node.left:
stack.append(node.left)
if node.right:
stack.append(node.right)
return root
6. 判断是否为平衡二叉树
def is_balanced(root):
def get_height(node):
if not node:
return 0
left_height = get_height(node.left)
right_height = get_height(node.right)
# 如果子树不平衡,返回 -1
if left_height == -1 or right_height == -1:
return -1
# 如果左右子树高度差大于1,不平衡
if abs(left_height - right_height) > 1:
return -1
return 1 + max(left_height, right_height)
return get_height(root) != -1
7. 判断是否为完全二叉树
def is_complete_tree(root):
if not root:
return True
queue = [root]
found_null = False
while queue:
node = queue.pop(0)
if not node:
found_null = True
else:
# 如果之前已经遇到过空节点,说明不是完全二叉树
if found_null:
return False
queue.append(node.left)
queue.append(node.right)
return True
二叉搜索树操作
1. 验证二叉搜索树
def is_valid_bst(root):
def validate(node, min_val, max_val):
if not node:
return True
if node.val <= min_val or node.val >= max_val:
return False
return validate(node.left, min_val, node.val) and \
validate(node.right, node.val, max_val)
return validate(root, float('-inf'), float('inf'))
def is_valid_bst_inorder(root):
"""利用中序遍历的特性"""
prev = None
def inorder(node):
nonlocal prev
if not node:
return True
if not inorder(node.left):
return False
if prev is not None and node.val <= prev:
return False
prev = node.val
return inorder(node.right)
return inorder(root)
2. 二叉搜索树的插入
def insert_into_bst(root, val):
if not root:
return TreeNode(val)
if val < root.val:
root.left = insert_into_bst(root.left, val)
else:
root.right = insert_into_bst(root.right, val)
return root
def insert_into_bst_iterative(root, val):
"""迭代实现"""
if not root:
return TreeNode(val)
node = root
while node:
if val < node.val:
if not node.left:
node.left = TreeNode(val)
break
node = node.left
else:
if not node.right:
node.right = TreeNode(val)
break
node = node.right
return root
3. 二叉搜索树的删除
def delete_node(root, key):
if not root:
return None
if key < root.val:
root.left = delete_node(root.left, key)
elif key > root.val:
root.right = delete_node(root.right, key)
else:
# 要删除的节点找到了
if not root.left:
return root.right
if not root.right:
return root.left
# 有两个子节点,找到右子树的最小节点(或左子树的最大节点)
min_node = get_min(root.right)
root.val = min_node.val
root.right = delete_node(root.right, min_node.val)
return root
def get_min(node):
while node.left:
node = node.left
return node
4. 查找二叉搜索树中的节点
def search_bst(root, val):
if not root or root.val == val:
return root
if val < root.val:
return search_bst(root.left, val)
else:
return search_bst(root.right, val)
常见算法题
题1:二叉树的最大路径和
题目描述: 给定一个二叉树,找到路径上节点值的最大和。路径可以从任意节点开始,到任意节点结束。
def max_path_sum(root):
max_sum = float('-inf')
def max_gain(node):
nonlocal max_sum
if not node:
return 0
# 递归计算左右子树的最大贡献值
# 如果贡献值为负,则不选择该路径
left_gain = max(max_gain(node.left), 0)
right_gain = max(max_gain(node.right), 0)
# 当前节点的最大路径和
price_newpath = node.val + left_gain + right_gain
# 更新全局最大值
max_sum = max(max_sum, price_newpath)
# 返回当前节点的最大贡献值
return node.val + max(left_gain, right_gain)
max_gain(root)
return max_sum
题2:二叉树的最近公共祖先(LCA)
题目描述: 给定一个二叉树,找到两个节点的最近公共祖先。
def lowest_common_ancestor(root, p, q):
if not root or root == p or root == q:
return root
left = lowest_common_ancestor(root.left, p, q)
right = lowest_common_ancestor(root.right, p, q)
# 如果左右子树都找到了,说明当前节点就是LCA
if left and right:
return root
# 如果只有一个子树找到了,返回那个结果
return left if left else right
二叉搜索树的LCA(更简单):
def lowest_common_ancestor_bst(root, p, q):
if p.val < root.val and q.val < root.val:
return lowest_common_ancestor_bst(root.left, p, q)
elif p.val > root.val and q.val > root.val:
return lowest_common_ancestor_bst(root.right, p, q)
else:
return root
题3:从前序与中序遍历序列构造二叉树
题目描述: 根据前序遍历和中序遍历的结果,构造二叉树。
def build_tree(preorder, inorder):
if not preorder or not inorder:
return None
# 前序遍历的第一个元素是根节点
root_val = preorder[0]
root = TreeNode(root_val)
# 在中序遍历中找到根节点的位置
root_index = inorder.index(root_val)
# 递归构建左右子树
root.left = build_tree(
preorder[1:root_index + 1],
inorder[:root_index]
)
root.right = build_tree(
preorder[root_index + 1:],
inorder[root_index + 1:]
)
return root
def build_tree_optimized(preorder, inorder):
"""优化版本:使用哈希表加速查找"""
inorder_map = {val: idx for idx, val in enumerate(inorder)}
pre_idx = 0
def build(left, right):
nonlocal pre_idx
if left > right:
return None
root_val = preorder[pre_idx]
root = TreeNode(root_val)
pre_idx += 1
root_index = inorder_map[root_val]
root.left = build(left, root_index - 1)
root.right = build(root_index + 1, right)
return root
return build(0, len(inorder) - 1)
题4:从中序与后序遍历序列构造二叉树
题目描述: 根据中序遍历和后序遍历的结果,构造二叉树。
def build_tree_from_inorder_postorder(inorder, postorder):
if not inorder or not postorder:
return None
# 后序遍历的最后一个元素是根节点
root_val = postorder[-1]
root = TreeNode(root_val)
# 在中序遍历中找到根节点的位置
root_index = inorder.index(root_val)
# 递归构建左右子树
root.left = build_tree_from_inorder_postorder(
inorder[:root_index],
postorder[:root_index]
)
root.right = build_tree_from_inorder_postorder(
inorder[root_index + 1:],
postorder[root_index:-1]
)
return root
题5:二叉树的序列化与反序列化
题目描述: 将二叉树序列化为字符串,并能够反序列化回二叉树。
def serialize(root):
"""使用前序遍历序列化"""
if not root:
return '#'
return str(root.val) + ',' + serialize(root.left) + ',' + serialize(root.right)
def deserialize(data):
"""反序列化"""
def build():
val = vals.pop(0)
if val == '#':
return None
node = TreeNode(int(val))
node.left = build()
node.right = build()
return node
vals = data.split(',')
return build()
题6:二叉树的右视图
题目描述: 给定一个二叉树,返回从右侧看到的节点值。
def right_side_view(root):
if not root:
return []
result = []
queue = [root]
while queue:
# 获取当前层的最后一个节点
result.append(queue[-1].val)
size = len(queue)
for _ in range(size):
node = queue.pop(0)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return result
题7:二叉树的锯齿形层序遍历
题目描述: 给定一个二叉树,返回其节点值的锯齿形层序遍历(先从左往右,再从右往左)。
def zigzag_level_order(root):
if not root:
return []
result = []
queue = [root]
left_to_right = True
while queue:
level = []
size = len(queue)
for _ in range(size):
node = queue.pop(0)
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
if not left_to_right:
level.reverse()
result.append(level)
left_to_right = not left_to_right
return result
题8:路径总和
题目描述: 给定一个二叉树和一个目标和,判断是否存在从根节点到叶子节点的路径,使得路径上所有节点值相加等于目标和。
def has_path_sum(root, target_sum):
if not root:
return False
# 如果是叶子节点,检查是否等于目标和
if not root.left and not root.right:
return root.val == target_sum
# 递归检查左右子树
return has_path_sum(root.left, target_sum - root.val) or \
has_path_sum(root.right, target_sum - root.val)
题9:路径总和 II
题目描述: 给定一个二叉树和一个目标和,找到所有从根节点到叶子节点路径总和等于给定目标和的路径。
def path_sum(root, target_sum):
result = []
def dfs(node, path, remaining):
if not node:
return
path.append(node.val)
remaining -= node.val
# 如果是叶子节点且路径和等于目标和
if not node.left and not node.right and remaining == 0:
result.append(path[:])
dfs(node.left, path, remaining)
dfs(node.right, path, remaining)
# 回溯
path.pop()
dfs(root, [], target_sum)
return result
题10:二叉树展开为链表
题目描述: 给定一个二叉树,原地将它展开为一个单链表(前序遍历顺序)。
def flatten(root):
if not root:
return
# 递归处理左右子树
flatten(root.left)
flatten(root.right)
# 保存右子树
right = root.right
# 将左子树移到右边
root.right = root.left
root.left = None
# 找到新右子树的末尾
curr = root
while curr.right:
curr = curr.right
# 将原来的右子树接到末尾
curr.right = right
题11:填充每个节点的下一个右侧节点指针
题目描述: 给定一个完美二叉树,填充它的每个 next 指针,让这个指针指向其下一个右侧节点。
class Node:
def __init__(self, val=0, left=None, right=None, next=None):
self.val = val
self.left = left
self.right = right
self.next = next
def connect(root):
if not root:
return None
leftmost = root
while leftmost.left:
head = leftmost
while head:
# 连接1:同一个父节点的左右子节点
head.left.next = head.right
# 连接2:不同父节点的相邻节点
if head.next:
head.right.next = head.next.left
head = head.next
leftmost = leftmost.left
return root
题12:二叉搜索树中的第K小元素
题目描述: 给定一个二叉搜索树,找到其中第 k 小的元素。
def kth_smallest(root, k):
stack = []
curr = root
while curr or stack:
while curr:
stack.append(curr)
curr = curr.left
curr = stack.pop()
k -= 1
if k == 0:
return curr.val
curr = curr.right
题13:验证二叉树的前序序列化
题目描述: 给定一个用逗号分隔的字符串,验证它是否是一个正确的二叉树前序序列化。
def is_valid_serialization(preorder):
slots = 1 # 初始化可用槽位数为1(根节点)
for node in preorder.split(','):
slots -= 1 # 消耗一个槽位
if slots < 0:
return False
if node != '#':
slots += 2 # 非空节点增加两个槽位(左右子节点)
return slots == 0
题14:二叉树的层平均值
题目描述: 给定一个二叉树,返回每层的平均值。
def average_of_levels(root):
if not root:
return []
result = []
queue = [root]
while queue:
level_sum = 0
size = len(queue)
for _ in range(size):
node = queue.pop(0)
level_sum += node.val
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(level_sum / size)
return result
题15:二叉树的最大宽度
题目描述: 给定一个二叉树,返回树的最大宽度(同一层最左和最右节点之间的距离)。
def width_of_binary_tree(root):
if not root:
return 0
max_width = 0
queue = [(root, 0)] # (node, position)
while queue:
level_size = len(queue)
_, first_pos = queue[0]
for _ in range(level_size):
node, pos = queue.pop(0)
if node.left:
queue.append((node.left, 2 * pos))
if node.right:
queue.append((node.right, 2 * pos + 1))
# 计算当前层的宽度
if queue:
_, last_pos = queue[-1]
max_width = max(max_width, last_pos - first_pos + 1)
return max_width
常见面试题
1. 如何判断一个二叉树是否为完全二叉树?
使用层序遍历,如果遇到空节点,则之后不能再有非空节点。
def is_complete_tree(root):
if not root:
return True
queue = [root]
found_null = False
while queue:
node = queue.pop(0)
if not node:
found_null = True
else:
if found_null:
return False
queue.append(node.left)
queue.append(node.right)
return True
2. 如何判断一个二叉树是否为满二叉树?
满二叉树满足:节点数 = 2^深度 - 1
def is_full_tree(root):
def get_depth_and_count(node):
if not node:
return 0, 0
left_depth, left_count = get_depth_and_count(node.left)
right_depth, right_count = get_depth_and_count(node.right)
depth = 1 + max(left_depth, right_depth)
count = 1 + left_count + right_count
return depth, count
depth, count = get_depth_and_count(root)
return count == (1 << depth) - 1
3. 如何求二叉树中两个节点的距离?
先找到最近公共祖先,然后计算两个节点到LCA的距离之和。
def distance_between_nodes(root, p, q):
def find_lca(node, p, q):
if not node or node == p or node == q:
return node
left = find_lca(node.left, p, q)
right = find_lca(node.right, p, q)
if left and right:
return node
return left if left else right
def get_distance(node, target, dist):
if not node:
return -1
if node == target:
return dist
left = get_distance(node.left, target, dist + 1)
if left != -1:
return left
return get_distance(node.right, target, dist + 1)
lca = find_lca(root, p, q)
dist1 = get_distance(lca, p, 0)
dist2 = get_distance(lca, q, 0)
return dist1 + dist2
4. 如何找到二叉树中最深的叶子节点?
使用层序遍历,记录最后一层的节点。
def find_deepest_leaves(root):
if not root:
return []
queue = [root]
while queue:
level = []
size = len(queue)
for _ in range(size):
node = queue.pop(0)
level.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
return level
5. 如何统计二叉树中节点的个数?
def count_nodes(root):
if not root:
return 0
return 1 + count_nodes(root.left) + count_nodes(root.right)
6. 如何统计二叉树中叶子节点的个数?
def count_leaf_nodes(root):
if not root:
return 0
if not root.left and not root.right:
return 1
return count_leaf_nodes(root.left) + count_leaf_nodes(root.right)
7. 如何找到二叉树中所有路径?
def all_paths(root):
result = []
def dfs(node, path):
if not node:
return
path.append(node.val)
if not node.left and not node.right:
result.append(path[:])
else:
dfs(node.left, path)
dfs(node.right, path)
path.pop()
dfs(root, [])
return result
8. 如何判断一个数组是否为二叉搜索树的后序遍历结果?
def verify_postorder(postorder):
def verify(left, right):
if left >= right:
return True
# 后序遍历的最后一个元素是根节点
root_val = postorder[right]
# 找到左右子树的分界点
i = left
while i < right and postorder[i] < root_val:
i += 1
# 验证右子树的所有元素都大于根节点
j = i
while j < right:
if postorder[j] < root_val:
return False
j += 1
# 递归验证左右子树
return verify(left, i - 1) and verify(i, right - 1)
if not postorder:
return True
return verify(0, len(postorder) - 1)
总结
核心要点:
- 二叉树的基础概念:理解二叉树的结构和性质
- 不同类型的二叉树:满二叉树、完全二叉树、二叉搜索树、平衡二叉树等
- 遍历方式:前序、中序、后序、层序的递归和迭代实现
- 常见操作:求深度、判断类型、翻转、验证等
- 算法题:路径和、LCA、序列化、层序遍历变形等
面试重点:
- 二叉树的遍历(递归和迭代都要掌握)
- 二叉搜索树的操作和验证
- 最近公共祖先(LCA)
- 从遍历序列构造二叉树
- 路径相关问题
- 层序遍历及其变形
解题技巧:
- 递归思想:大多数二叉树问题都可以用递归解决
- 迭代替代递归:使用栈或队列实现迭代
- 前序遍历:根→左→右,适合自上而下处理
- 后序遍历:左→右→根,适合自下而上处理
- 中序遍历:左→根→右,对BST可以得到有序序列
- 层序遍历:使用队列,适合按层处理
参考资料:
- 《算法导论》
- 《数据结构与算法分析》
- LeetCode 二叉树专题
- 《剑指 Offer》
Originally published on mlangTse's Blog. View source