Leetcode二叉树刷题总结(个人向,代码随想录题单)

二叉树部分刷题章节总结

一,二叉树基础与遍历

  1. 思路归纳: 递归 & 迭代,DFS & BFS 在二叉树中的实现
  2. 题单:
二叉树的前序遍历
二叉树的中序遍历
二叉树的后序遍历
二叉树的层序遍历
  1. 核心代码实现:

二叉树的三种遍历方式(前中后序)的递归实现思路类似,如前序遍历实现的核心代码如下:

def preorder(root):    # 根 → 左 → 右
    if not root:
        return
    print(root.val)    # 根
    preorder(root.left)
    preorder(root.right)

中序和后序只需要调整根节点访问位置即可。

而前中后序的迭代法的具体实现方式则各有不同。不过本质都是手动模拟栈/队列
,前序的迭代法核心代码如下:

def preorderTraversal(root: Optional[TreeNode]) -> List[int]:
    result = []
    myStack = []

    if root is not None:
        myStack.append(root)

    while myStack:
        node = myStack.pop()
        result.append(node.val)

        if node.right is not None:
            myStack.append(node.right)

        if node.left is not None:
            myStack.append(node.left)

    return result

后序的迭代写法与前序类似,将前序迭代写法调换入栈顺序,并将结果进行反转即可。

中序的迭代写法与前序/后序的迭代写法不同,原因在于当前遍历的节点与待处理的节点不同

def preorderTraversal(root: Optional[TreeNode]) -> List[int]:
    result = []
    myStack = []

    cur = root
    while cur or myStack:
        # 一路遍历到最左叶子节点
        if cur:
            myStack.append(cur)
            cur = cur.left
        # 从最左叶子节点一路向上,处理中间节点和右节点
        else:
            cur = myStack.pop()
            result = cur.val
            cur = cur.right

    return result

我们可以通过标志位法统一中序遍历和后续遍历的迭代写法,以下给出中序遍历的模板,后序遍历的标志位法只需要在此基础上改换节点处理顺序即可。我们通过标志位,解决了中序和后序遍历中遍历的节点和待处理节点不相同的问题。由标志位表示一个节点是否应该被 "收割"

def inorderTraversal(root: Optional[TreeNode]) -> List[int]:
    result = []
    myStack = []

    if root is None:
        return result

    myStack.append([root, False])
    while myStack:
        node, visited = myStack.pop()

        if visited:
            result.append(node.val)
            continue

        if node.right is not None:
            myStack.append([node.right, False])

        myStack.append([node, True])

        if node.left is not None:
            myStack.append([node.left, False])

    return result

二叉树的层序遍历利用队列存储节点,每次处理一层的所有节点,核心代码如下:

def levelOrder(root: Optional[TreeNode]) -> List[List[int]]:
    from collections import deque
    result = []
    myQueue = deque()

    if root is not None:
        myQueue.append(root)

    while myQueue:
        # 队列长度即为当前层元素的数量,逐层进行处理
        size = len(myQueue)
        levelResult = []

        for _ in range(size):
            node = myQueue.popleft()
            levelResult.append(node.val)
            # 将当前节点的左右节点加入到队列中
            if node.left:
                myQueue.append(node.left)

            if node.right:
                myQueue.append(node.right)

        result.append(levelResult)

    return result
  1. 小结: 遍历是解题套路的入口
  • 如果题目涉及 “统计/判断子树属性” → 大概率用后序
  • 如果题目涉及 “搜索目标/路径” → 常用前序/DFS
  • 如果题目涉及 “顺序性(如二叉搜索树相关问题)” → 常用中序
  • 如果题目涉及 “按层分析” → 用层序

二,二叉树的属性问题

  1. 思路归纳
  • 深度/高度:递归返回值就是子树高度 → 典型的自底向上分治 → 后序遍历
  • 平衡性:在计算高度的同时,额外返回一个状态(是否平衡)→ 后序遍历
  • 对称性:需要同时比较两颗子树 → 自顶向下进行 “双指针递归”
  1. 题单
二叉树最大深度104
二叉树最小深度111
平衡二叉树110
对称二叉树101
  1. 核心代码实现

A. 高度类问题(二叉树最大高度/最小高度)

def getDepth(root):
    if not root:
        return 0
    left = getDepth(root.left)
    right = getDepth(root.right)
    return 1 + max(left, right)   # 最大深度
    # return 1 + min(left, right) # 最小深度 (注意叶子判断)

B. 平衡类问题(平衡二叉树)

def getHeight(root):
    if not root:
        return 0
    left = getHeight(root.left)
    right = getHeight(root.right)
    if left == -1 or right == -1 or abs(left - right) > 1:
        return -1   # -1 作为“不平衡”标记
    return 1 + max(left, right)

def isBalanced(root):
    return getHeight(root) != -1

C. 对称性问题(对称二叉树)

def isMirror(p, q):
    if not p and not q:
        return True
    if not p or not q:
        return False

    return (p.val == q.val and
            isMirror(p.left, q.right) and
            isMirror(p.right, q.left))

def isSymmetric(root):
    if not root:
        return True

    return isMirror(root.left, root.right)
  1. 小结

这三个题型的本质:

  • 深度 → 返回值为高度
  • 平衡 → 返回值为高度 或 标志位
  • 对称 → 双指针递归,对应的节点成对比较

三,二叉树的路径相关问题

  1. 思路归纳
  • 路径枚举:DFS 深度优先遍历,携带路径,遇到叶子结点记录 → 典型的回溯法。
  • 路径和:递归时传递剩余目标值,直至叶子判断 → Top-down 参数传递。 路径最值:记录遍历过程中符合条件的最优结果(如左下角值)。
  • 路径统计:在递归中累积结果,或在回溯时统计。

套路关键词:DFS + 回溯,结合 参数传递 和 条件判断。

  1. 题单
二叉树所有路径 257
路径总和 112
左叶子之和 404
找树左下角的值 513
  1. 核心代码实现

A. 路径枚举(二叉树所有路径)

def binaryTreePaths(root):
    res, path = [], []

    def dfs(node):
        if not node:
            return
        path.append(str(node.val))
        if not node.left and not node.right:   # 当前节点为空,说明已经形成一条完整的路径
            res.append("->".join(path))
        else:
            dfs(node.left)
            dfs(node.right)
        path.pop()   # 回溯

    dfs(root)
    return res

B. 路径和

def hasPathSum(root, targetSum):
    if not root:
        return False
    if not root.left and not root.right:
        return targetSum == root.val
    return (hasPathSum(root.left, targetSum - root.val) or
            hasPathSum(root.right, targetSum - root.val))

C. 左叶子之和

def sumOfLeftLeaves(root):
    if not root:
        return 0
    res = 0
    if root.left and not root.left.left and not root.left.right:
        res += root.left.val
    return res + sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right)

D. 左下角的值

def findBottomLeftValue(root):
    from collections import deque
    q = deque([root])
    while q:
        node = q.popleft()
        # 先右后左,保证最后一个访问的就是最左叶子
        if node.right:
            q.append(node.right)
        if node.left:
            q.append(node.left)
    return node.val
  1. 小结
  • 枚举路径 → DFS + 回溯,核心是 path.append() / path.pop()。
  • 路径和 → 自顶向下参数传递,叶子结点判断。
  • 路径最值 → BFS/DFS 均可,用遍历顺序控制结果。

路径类问题的关键就是 遍历到叶子结点时做判断或记录。

四,二叉树的构造与修改

  1. 思路归纳
  • 根据遍历序列构造树:核心是找到 根节点 → 根据遍历数组分割左右子树 → 递归构造。
  • 数组转 BST:利用数组有序性,取中点为根,递归左右构建。
  • 树的修改/合并:递归对称操作,遇到空节点时特殊处理。
  • 翻转树:交换左右子树 → 递归到底。

套路关键词:分治 + 递归构造

  1. 题单
从中序与后序遍历序列构造二叉树 106
最大二叉树 654
有序数组转化为二叉搜索树 108
合并二叉树 617
翻转二叉树 226
  1. 核心代码实现

A. 根据中序+后序构造二叉树

def buildTree(inorder, postorder):
    if not inorder:
        return None

    root_val = postorder.pop()
    root = TreeNode(root_val)

    # 此处仅作为核心代码演示,具体实现时可以传递数组下标以节省资源,而不是为相应数组片段新创建一个数组
    idx = inorder.index(root_val)
    root.left = buildTree(inorder[:idx], postorder[:idx])
    root.right = buildTree(inorder[idx+1:], postorder[idx:])
    return root

后序数组的最后一个元素为根节点,根据中序数组的分割,可以分别得到左右子树的中序数组和后序数组,递归构造左右子树即可。

B. 最大二叉树

def constructMaximumBinaryTree(nums):
    if not nums:
        return None
    max_idx = nums.index(max(nums))

    # 不断找出最大值分割数组,进行分治
    # 同样,具体实现时可以传递数组下标
    root = TreeNode(nums[max_idx])
    root.left = constructMaximumBinaryTree(nums[:max_idx])
    root.right = constructMaximumBinaryTree(nums[max_idx+1:])
    return root

C. 有序数组转化为BST

def sortedArrayToBST(nums):
    if not nums:
        return None
    mid = len(nums) // 2

    # 根据二叉平衡树的性质,每次只需要选取数组中间的元素作为根节点即可
    # 重复该过程递归构造
    root = TreeNode(nums[mid])
    root.left = sortedArrayToBST(nums[:mid])
    root.right = sortedArrayToBST(nums[mid+1:])
    return root

D. 合并二叉树

def merge(root1: Optional[TreeNode], root2: Optional[TreeNode]) -> Optional[TreeNode]:
    if root1 is None and root2 is None:
        return

    # 在树1原地对节点的值进行修改
    if root1 and root2:
        root1.val += root2.val
        root1.left = merge(root1.left, root2.left)
        root1.right = merge(root1.right, root2.right)
        return root1

    # 二者仅存其一,直接返回即可
    if root1:
        return root1

    return root2

E. 翻转二叉树


def invertTree(root: Optional[TreeNode]) -> Optional[TreeNode]:

    def traverse(cur: Optional[TreeNode]) -> None:
        if cur is None:
            return

        # 交换两个子树后,继续向下递归
        cur.left, cur.right = cur.right, cur.left
        traverse(cur.left)
        traverse(cur.right)

    traverse(root)
    return root
  1. 小结
  • 构造类问题:本质是分治,找到根 → 分割子树 → 递归构造。
  • 修改类问题:递归处理左右子树,注意 空节点边界。
  • 翻转、合并这种问题考察的是 递归对称性。

五,二叉搜索树专题

  1. 思路归纳
  • BST 特性:中序遍历有序 → 几乎所有题目都依赖这一性质。
  • 二叉搜索树的搜索/插入/删除:递归或迭代,关键在于比较大小后走左/右子树。
  • 修剪:利用区间范围,剪去不满足条件的子树。
  • 最近公共祖先:利用 BST 有序性,可以快速定位分叉点。
  • 最小差值/众数:中序遍历序列有序 → 顺序比较或计数。
  • 转化为累加树:倒序中序遍历 + 累加和。

套路关键词:中序有序性 + 递归 / 迭代。

  1. 题单
二叉搜索树中的搜索 700
二叉搜索树中的插入操作 701
删除二叉搜索树中的节点 450
修剪二叉搜索树 669
二叉搜索树的最近公共祖先 235
二叉搜索树的最小绝对差 530
二叉搜索树中的众数 501
把二叉搜索树转换为累加树 538
  1. 核心代码实现

A. 二叉搜索树的搜索/插入操作

def searchBST(root, val):
    if not root or root.val == val:
        return root
    if val < root.val:
        return searchBST(root.left, val)
    else:
        return searchBST(root.right, val)

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

利用BST左小右大的特性,实现快速递归定位

B. 验证二叉搜索树

def isValidBST(root: Optional[TreeNode]) -> bool:
    prev = None

    def dfs(node: Optional[TreeNode]) -> bool:
        nonlocal prev
        if not node:
            return True
        if not dfs(node.left):
            return False
        # 当前节点小于等于前一个节点,说明当前二叉树不符合二叉搜索树的性质,终止递归
        if prev is not None and node.val <= prev:
            return False
        prev = node.val
        return dfs(node.right)

    return dfs(root)

C. 删除操作

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

        # 找到右子树最小节点,将被删除节点的左节点作为该右子树最小节点的左子节点
        node = root.right
        while node.left:
            node = node.left
        root.val = node.val
        root.right = deleteNode(root.right, node.val)
    return root

删除操作相对而言更加复杂,删除一个节点后,为了保持二叉搜索树的性质,需要考虑三种情况: 只有左子树/只有右子树/左右子树都存在

D. 修建BST

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

由于二叉搜索树的性质,直接根据区间 [low, high] 进行剪枝即可,递归处理左右子树。需要注意的是,若当前根节点小于给定区间的最小值,或大于给定区间的最大值时,其子树仍然可能满足条件,需要继续向相应子树方向递归。

E. 最近公共祖先

def lowestCommonAncestor(root, p, q):
    if not root:
        return None
    if p.val < root.val and q.val < root.val:
        return lowestCommonAncestor(root.left, p, q)
    if p.val > root.val and q.val > root.val:
        return lowestCommonAncestor(root.right, p, q)
    return root

当一个节点的值首次在区间 [p.val,q.val] 内时,该节点即为两个 p, q 的最近公共祖先

F. 二叉搜索树中节点的最小绝对差

def getMinimumDifference(root: Optional[TreeNode]) -> int:
    from collections import deque
    myQueue = deque()

    myQueue.append([root, False])
    pre = None

    minDiff = float('inf')

    while myQueue:
        node, visited = myQueue.pop()
        if visited:
            cur = node
            if pre is None:
                pass

            elif cur.val - pre.val < minDiff:
                minDiff = cur.val - pre.val

            pre = cur
            continue

        if node.right:
            myQueue.append([node.right, False])

        myQueue.append([node, True])

        if node.left:
            myQueue.append([node.left, False])

    return minDiff

利用 BST 的 中序有序性,相邻节点差值最小。

此处给出的实现参考运用迭代法,在中序遍历的过程中,即实时更新节点之间的最小绝对差。在具体实现时,为了方便理解,也可以使用递归法先获取二叉搜索树的中序遍历序列,然后再逐一比较两个元素之间的最小绝对差

G. BST转换为累加树


def convertBST(root: Optional[TreeNode]) -> Optional[TreeNode]:

    def traverse(cur: Optional[TreeNode], sum: int) -> int:
        if cur is None:
            return sum

        right_sum = traverse(cur.right, sum)

        val = cur.val + right_sum
        cur.val = val

        return traverse(cur.left, val)

    traverse(root, 0)
    return root

倒序中序遍历(右 → 根 → 左),累加更新。

  1. 小结
  • 搜索 / 插入 / 删除:体现了 BST 的结构特性。
  • 修剪 / LCA:利用 BST 有序性,省去不必要的递归。
  • 最小差值 / 众数:中序遍历特性,保证有序性。
  • 转化为累加树:倒序中序 → 累加和。

一遇到BST问题,第一反应是中序遍历有序性

六,完全二叉树(待后续补充)

  1. 思路归纳
  • 完全二叉树特性: 叶子节点只能出现在最下层和次下层,且最下层的叶子节点集中在树的最左边
  • 完全二叉树的节点数量: 完全二叉树中包含了满二叉树子树,利用满二叉树子树快速计算节点的个数。
  1. 题单
完全二叉树节点个数 222
  1. 核心代码实现

A. 完全二叉树节点个数

def countNodes(root: Optional[TreeNode]) -> int:
    if root is None:
        return 0

    leftDepth, rightDepth = 1, 1
    leftNode, rightNode = cur.left, cur.right
    while leftNode:
        leftDepth += 1
        leftNode = leftNode.left
    while rightNode:
        rightDepth += 1
        rightNode = rightNode.right

    # 以当前节点为根节点的二叉树为满二叉树,因为左右叶子深度相同,直接返回其节点数量
    if leftDepth == rightDepth:
        return 2 ** rightDepth - 1

    # 返回左右子树节点数量之和
    return 1 + getCount(root.left) + getCount(root.right)

七,总结

二叉树思路整体归纳

  • 三大解题套路
  1. 自底向上(Bottom-up)

    • 典型应用:子树属性问题(深度、高度、平衡、LCA 普通树)
    • 关键:递归返回子树信息,逐层汇总
  2. 自顶向下(Top-down)

    • 典型应用:路径问题、带约束的统计(路径和、左叶子之和)
    • 关键:递归传递参数 → 上层控制下层遍历
  3. 回溯(Backtracking / DFS)

    • 典型应用:方案枚举问题(路径枚举、子集、排列问题)
    • 关键:递归 + 状态回退
  • BST 与普通二叉树的区别
    • BST 核心性质:中序遍历严格递增
    • 利用有序性可以 剪枝 / 快速定位 / 优化遍历
    • 普通二叉树不具备有序性 → 必须全局搜索或自底向上汇总信息
类型 典型题 推荐套路
子树属性问题 深度、高度、平衡、对称 Bottom-up
路径 / 目标问题 路径和、左叶子之和、左下角值 Top-down
方案枚举 路径枚举、排列组合 DFS + 回溯
BST 问题 搜索 / 插入 / 删除 / LCA / 修剪 利用中序有序性
posted @ 2025-08-26 15:10  雪痕春风天音九重色  阅读(4)  评论(0)    收藏  举报