All articles

二叉树原理及常见算法题

梳理二叉树性质、遍历方式、搜索树与高频算法题的思路和实现。

2025-10-26 · Updated 2025-11-24 · 21 分钟阅读

二叉树基础

什么是二叉树?

二叉树(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

二叉树的性质

  1. 节点数与深度的关系

    • 第 i 层最多有 2^(i-1) 个节点(i ≥ 1)
    • 深度为 k 的二叉树最多有 2^k - 1 个节点
  2. 叶子节点数

    • n0 = n2 + 1(n0 为叶子节点数,n2 为度为 2 的节点数)
  3. 完全二叉树的性质

    • 具有 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 的特点:

  1. 多路平衡树:每个节点可以有多个子节点(通常称为 m 阶 B+Tree)
  2. 所有数据存储在叶子节点:非叶子节点只存储键值(索引)
  3. 叶子节点通过指针连接:形成有序链表,方便范围查询
  4. 所有叶子节点在同一层:保证平衡性

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-TreeB+Tree
数据存储所有节点都可以存储数据只有叶子节点存储数据
非叶子节点存储键值和数据只存储键值(索引)
叶子节点不连接通过指针连接成链表
查找效率可能在任何层找到数据必须到叶子节点
范围查询需要中序遍历通过链表顺序访问
空间利用率较低较高(非叶子节点更小)

B+Tree 的优势:

  1. 适合磁盘存储

    • 节点大小通常等于磁盘页大小(如 4KB)
    • 减少磁盘 I/O 次数
    • 非叶子节点可以存储更多键值
  2. 范围查询高效

    • 叶子节点形成有序链表
    • 范围查询只需遍历链表,不需要回溯
  3. 查询稳定

    • 所有查询都要到叶子节点
    • 查询路径长度相同,性能稳定

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  # 未找到

插入:

  1. 找到应该插入的叶子节点
  2. 如果叶子节点未满,直接插入
  3. 如果叶子节点已满,进行分裂:
    • 将节点分成两部分
    • 中间键值提升到父节点
    • 如果父节点也满,继续向上分裂

删除:

  1. 找到包含要删除键值的叶子节点
  2. 删除键值
  3. 如果节点键值数量低于最小值,进行合并或借用:
    • 尝试从兄弟节点借用
    • 如果无法借用,与兄弟节点合并

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)

总结

核心要点:

  1. 二叉树的基础概念:理解二叉树的结构和性质
  2. 不同类型的二叉树:满二叉树、完全二叉树、二叉搜索树、平衡二叉树等
  3. 遍历方式:前序、中序、后序、层序的递归和迭代实现
  4. 常见操作:求深度、判断类型、翻转、验证等
  5. 算法题:路径和、LCA、序列化、层序遍历变形等

面试重点:

  • 二叉树的遍历(递归和迭代都要掌握)
  • 二叉搜索树的操作和验证
  • 最近公共祖先(LCA)
  • 从遍历序列构造二叉树
  • 路径相关问题
  • 层序遍历及其变形

解题技巧:

  1. 递归思想:大多数二叉树问题都可以用递归解决
  2. 迭代替代递归:使用栈或队列实现迭代
  3. 前序遍历:根→左→右,适合自上而下处理
  4. 后序遍历:左→右→根,适合自下而上处理
  5. 中序遍历:左→根→右,对BST可以得到有序序列
  6. 层序遍历:使用队列,适合按层处理

参考资料:

  • 《算法导论》
  • 《数据结构与算法分析》
  • LeetCode 二叉树专题
  • 《剑指 Offer》

Originally published on mlangTse's Blog. View source