1. 二叉树专题:LeetCode 110-98题精解
作为数据结构中最基础的非线性结构之一,二叉树在算法面试中出现的频率居高不下。最近在整理LeetCode题库时,我发现110到98题号区间内集中了多个经典的二叉树问题,涵盖了平衡判断、路径搜索、构造还原等核心考点。本文将针对这10道题目(110、257、404、513、112、106、654、617、700、98)进行系统解析,分享我的解题思路和优化技巧。
这些题目看似分散,实则存在内在联系。例如110题(平衡二叉树)需要深度计算,而404题(左叶子之和)需要特殊节点判断,二者都可以通过深度优先搜索的变体来解决。通过对比练习,能够帮助我们建立二叉树的通用解题框架。本文特别适合已经掌握二叉树基本遍历方式,希望提升面试解题效率的开发者。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基础题型分析与解法模板
2.1 平衡二叉树判断(LeetCode 110)
判断二叉树是否平衡的标准是:每个节点的左右子树高度差不超过1。这个定义本身就直接提示了递归解法:
python复制def isBalanced(root):
def height(node):
if not node:
return 0
left = height(node.left)
right = height(node.right)
if left == -1 or right == -1 or abs(left - right) > 1:
return -1
return max(left, right) + 1
return height(root) != -1
关键点在于将高度计算与平衡判断合二为一。当发现不平衡时立即返回-1终止递归,避免重复计算。时间复杂度O(n),每个节点仅访问一次。
实际面试中,面试官常会追问非递归解法。可以使用后序遍历的迭代写法,用哈希表记录每个节点的已计算高度。
2.2 二叉树所有路径(LeetCode 257)
收集从根节点到所有叶子的路径,本质上是深度优先搜索的应用:
python复制def binaryTreePaths(root):
def dfs(node, path):
if not node:
return
path += str(node.val)
if not node.left and not node.right:
res.append(path)
return
path += "->"
dfs(node.left, path)
dfs(node.right, path)
res = []
dfs(root, "")
return res
这里有个易错点:路径字符串的拼接时机。如果在递归调用后才拼接箭头符号,会导致末尾多出多余的"->"。我的经验是在递归前完成当前节点的处理。
3. 特殊节点处理技巧
3.1 左叶子节点之和(LeetCode 404)
左叶子的定义需要同时满足:
- 是父节点的左孩子
- 自身是叶子节点
python复制def sumOfLeftLeaves(root):
if not root:
return 0
def isLeaf(node):
return not node.left and not node.right
if root.left and isLeaf(root.left):
return root.left.val + sumOfLeftLeaves(root.right)
return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right)
这个解法展示了如何通过辅助函数增强代码可读性。注意右子树的递归调用不能省略,因为右子树中可能包含符合条件的左叶子。
3.2 找树左下角的值(LeetCode 513)
寻找最后一行最左边的值,可以通过层次遍历轻松解决:
python复制def findBottomLeftValue(root):
queue = [root]
while queue:
current = queue.pop(0)
if current.right: # 右节点先入队
queue.append(current.right)
if current.left:
queue.append(current.left)
return current.val
这里使用了一个小技巧:让右节点先入队。这样队列处理顺序就是从右到左,最后剩下的current自然就是最左侧节点。相比记录每层首节点的方法,空间复杂度相同但更简洁。
4. 路径与求和问题
4.1 路径总和(LeetCode 112)
判断是否存在从根到叶子的路径和等于目标值:
python复制def hasPathSum(root, targetSum):
if not root:
return False
if not root.left and not root.right:
return root.val == targetSum
return hasPathSum(root.left, targetSum - root.val) or
hasPathSum(root.right, targetSum - root.val)
这个解法展示了递归的优雅之处。注意边界条件:必须在叶子节点(而非空节点)判断是否满足和。常见错误是在遇到空节点时比较targetSum是否为0。
4.2 从中序与后序遍历序列构造二叉树(LeetCode 106)
经典的重建二叉树问题:
python复制def buildTree(inorder, postorder):
if not inorder:
return None
root_val = postorder[-1]
root = TreeNode(root_val)
idx = inorder.index(root_val)
root.left = buildTree(inorder[:idx], postorder[:idx])
root.right = buildTree(inorder[idx+1:], postorder[idx:-1])
return root
关键点在于确定左右子树的区间范围。后序的最后一个元素是根节点,中序中该元素左侧是左子树,右侧是右子树。注意切片操作不包括结束索引,因此postorder右子树范围是[idx:-1]而非[idx:-1]。
5. 特殊构造与应用
5.1 最大二叉树(LeetCode 654)
根据数组构造最大二叉树:
python复制def constructMaximumBinaryTree(nums):
if not nums:
return None
max_val = max(nums)
max_index = nums.index(max_val)
root = TreeNode(max_val)
root.left = constructMaximumBinaryTree(nums[:max_index])
root.right = constructMaximumBinaryTree(nums[max_index+1:])
return root
这个构造过程与快速排序的分治思想类似。每次找当前区间的最大值作为根节点,然后递归处理左右子区间。时间复杂度O(n^2),可以通过单调栈优化到O(n)。
5.2 合并二叉树(LeetCode 617)
合并两棵二叉树的简单规则:
python复制def mergeTrees(t1, t2):
if not t1:
return t2
if not t2:
return t1
merged = TreeNode(t1.val + t2.val)
merged.left = mergeTrees(t1.left, t2.left)
merged.right = mergeTrees(t1.right, t2.right)
return merged
这个解法展示了如何处理两个树的同步遍历。不需要新建所有节点,当其中一个树为空时直接返回另一棵树的子树即可,这样可以节省空间。
6. 搜索二叉树专题
6.1 二叉搜索树中的搜索(LeetCode 700)
利用BST性质进行高效查找:
python复制def searchBST(root, val):
while root and root.val != val:
root = root.left if val < root.val else root.right
return root
迭代写法比递归更高效,因为不需要函数调用开销。BST的搜索过程就像二分查找,每次都能排除一半的候选节点。
6.2 验证二叉搜索树(LeetCode 98)
验证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)
这个解法通过维护值范围的上下界来确保BST性质。常见错误是仅比较节点与直接子节点的值,这样无法检测到子树中违规的节点。
7. 二叉树解题通用技巧
通过以上题目的练习,我总结出二叉树问题的几个通用解法:
-
递归三要素:终止条件、当前处理、递归调用。像110、112、404等题都符合这个模式。
-
遍历方式选择:
- 深度优先:适合路径相关(257、112)
- 层次遍历:适合层级相关(513)
- 特殊顺序:中序+后序重建(106)
-
空间优化:递归转迭代可以避免栈溢出,如700题的迭代解法。
-
边界处理:特别注意空节点和叶子节点的判断,这是大部分错误的来源。
在实际面试中,建议先明确问题性质,选择合适遍历方式,然后处理递归或迭代实现。记得讨论时间/空间复杂度,这往往是面试官的考察重点。
