二叉树标准答案¶
记忆核心:树题先问“递归函数返回什么”。需要全局答案时,在 DFS 过程中更新外层变量。
94 二叉树的中序遍历¶
- 题目:返回二叉树的中序遍历结果,顺序是左子树、根、右子树。
- 思路:递归先走左边,再记录当前节点,再走右边。
- 标准答案:
class Solution:
def inorderTraversal(self, root):
ans = []
def dfs(node):
if not node:
return
dfs(node.left)
ans.append(node.val)
dfs(node.right)
dfs(root)
return ans
- 小坑:中序不是层序;顺序固定是左、根、右。
98 验证二叉搜索树¶
- 题目:判断一棵树是否是二叉搜索树,要求每个节点都满足左边所有值更小,右边所有值更大。
- 思路:递归传上下界。左子树上界变成当前值,右子树下界变成当前值。
- 标准答案:
class Solution:
def isValidBST(self, root):
def dfs(node, low, high):
if not node:
return True
if not (low < node.val < high):
return False
return dfs(node.left, low, node.val) and dfs(node.right, node.val, high)
return dfs(root, float('-inf'), float('inf'))
- 小坑:不能只比较父子节点;要比较整棵子树的上下界。
101 对称二叉树¶
- 题目:判断一棵树是否关于中心镜像对称。
- 思路:比较两棵子树是否镜像:外侧对外侧,内侧对内侧。
- 标准答案:
class Solution:
def isSymmetric(self, root):
def check(a, b):
if not a and not b:
return True
if not a or not b:
return False
return a.val == b.val and check(a.left, b.right) and check(a.right, b.left)
return check(root.left, root.right)
- 小坑:镜像比较是
a.left对b.right,a.right对b.left。
102 二叉树的层序遍历¶
- 题目:按层返回二叉树节点值,每一层一个列表。
- 思路:队列 BFS;每层开始先固定当前队列长度。
- 标准答案:
from collections import deque
class Solution:
def levelOrder(self, root):
if not root:
return []
q = deque([root])
ans = []
while q:
level = []
for _ in range(len(q)):
node = q.popleft()
level.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
ans.append(level)
return ans
- 小坑:
len(q)要在每层开始时固定。
104 二叉树的最大深度¶
- 题目:返回二叉树从根到最远叶子的节点数。
- 思路:空树深度 0;非空树深度是左右最大深度加 1。
- 标准答案:
class Solution:
def maxDepth(self, root):
if not root:
return 0
return max(self.maxDepth(root.left), self.maxDepth(root.right)) + 1
- 小坑:深度按节点数算,空节点返回 0。
105 从前序与中序遍历序列构造二叉树¶
- 题目:给前序和中序遍历,还原原来的二叉树。
- 思路:前序第一个是根;中序里根左边是左子树,右边是右子树。
- 标准答案:
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> Optional[TreeNode]:
if len(inorder) == 0:
return None
root = TreeNode(preorder[0])
mid = inorder.index(preorder[0])
root.left = self.buildTree(preorder[1 : mid + 1], inorder[:mid])
root.right = self.buildTree(preorder[mid + 1:], inorder[mid + 1:])
return root
- 小坑:前序第一个就是根;
mid左边的中序是左子树,右边是右子树;前序也要按左子树长度切开。
108 将有序数组转换为二叉搜索树¶
- 题目:把升序数组转换成高度平衡的二叉搜索树。
- 思路:每次取中点当根,左半边建左子树,右半边建右子树。
- 标准答案:
class Solution:
def sortedArrayToBST(self, nums):
def build(l, r):
if l > r:
return None
mid = (l + r) // 2
root = TreeNode(nums[mid])
root.left = build(l, mid - 1)
root.right = build(mid + 1, r)
return root
return build(0, len(nums) - 1)
- 小坑:右子树从
mid + 1开始,不要再包含中点。
114 二叉树展开为链表¶
- 题目:把二叉树原地展开成前序遍历顺序的单链表,全部用右指针连接。
- 思路:递归拉平左右子树,再把左链插到根和原右链中间。
- 标准答案:
class Solution:
def flatten(self, root):
def dfs(node):
if not node:
return None
left_tail = dfs(node.left)
right_tail = dfs(node.right)
if left_tail:
left_tail.right = node.right
node.right = node.left
node.left = None
return right_tail or left_tail or node
dfs(root)
- 小坑:先让
left_tail.right接原右链,再把左链挪到右边。
124 二叉树中的最大路径和¶
- 题目:找树中任意一条路径的最大节点值之和,路径不一定经过根。
- 思路:DFS 返回“从当前节点往父节点能贡献的最大单边收益”;全局答案可以用左右两边加当前节点更新。
- 标准答案:
class Solution:
def maxPathSum(self, root):
ans = float('-inf')
def dfs(node):
nonlocal ans
if not node:
return 0
left = max(dfs(node.left), 0)
right = max(dfs(node.right), 0)
ans = max(ans, node.val + left + right)
return node.val + max(left, right)
dfs(root)
return ans
- 小坑:更新答案可以左右都要;返回给父节点只能选一边。
124 和 543 的区别¶
543 二叉树的直径:每条边/节点长度都默认是正贡献,所以左右高度都可以拿来更新答案;返回给父节点的是“继续往下走的最长高度”。124 二叉树中的最大路径和:节点值可能是负数,负贡献要主动丢掉,所以左右收益先写max(dfs(child), 0);返回给父节点时也只能返回单边,因为路径不能在父节点那里分叉两次。- 记忆句:
543数边数,左右高度天然能加;124算价值,负数要砍掉,返回只能单边。
199 二叉树的右视图¶
- 题目:从右侧看二叉树,返回每一层最右边的节点值。
- 思路:层序遍历,每层最后弹出的节点就是右视图。
- 标准答案:
from collections import deque
class Solution:
def rightSideView(self, root):
if not root:
return []
q = deque([root])
ans = []
while q:
size = len(q)
for i in range(size):
node = q.popleft()
if i == size - 1:
ans.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
return ans
- 小坑:右视图不是只沿右边走;缺右节点时可能看到左节点。
226 翻转二叉树¶
- 题目:把二叉树每个节点的左右子树交换。
- 思路:当前节点交换左右,然后递归处理左右子树。
- 标准答案:
class Solution:
def invertTree(self, root):
if not root:
return None
root.left, root.right = root.right, root.left
self.invertTree(root.left)
self.invertTree(root.right)
return root
- 小坑:要返回
root,不是返回左右子树。
230 二叉搜索树中第 K 小的元素¶
- 题目:在 BST 中找第
k小的节点值。 - 思路:BST 中序遍历是升序,第
k个就是答案。 - 标准答案:
class Solution:
def kthSmallest(self, root, k):
arr = []
def dfs(node):
if not node:
return
dfs(node.left)
arr.append(node.val)
dfs(node.right)
dfs(root)
return arr[k - 1]
- 小坑:第
k小对应下标k - 1。
236 二叉树的最近公共祖先¶
- 题目:给普通二叉树中的两个节点
p/q,找它们最近的公共祖先。 - 思路:左右子树分别找
p/q;两边都找到,当前节点就是答案。 - 标准答案:
class Solution:
def lowestCommonAncestor(self, root, p, q):
if not root or root == p or root == q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root
return left if left else right
- 小坑:普通二叉树不能用大小比较;递归方法前要写
self.。
437 路径总和 III¶
- 题目:统计树中有多少条向下路径的节点和等于目标值,路径不要求从根开始。
- 思路:树上前缀和。当前前缀和为
cur,之前出现过cur - target几次,就新增几条路径。 - 标准答案:
from collections import defaultdict
class Solution:
def pathSum(self, root, targetSum):
cnt = defaultdict(int)
cnt[0] = 1
def dfs(node, cur):
if not node:
return 0
cur += node.val
ans = cnt[cur - targetSum]
cnt[cur] += 1
ans += dfs(node.left, cur)
ans += dfs(node.right, cur)
cnt[cur] -= 1
return ans
return dfs(root, 0)
- 小坑:离开节点时要
cnt[cur] -= 1,防止左右分支互相污染。
543 二叉树的直径¶
- 题目:返回二叉树中任意两个节点之间最长路径的边数。
- 思路:DFS 返回当前节点向下的最长链长;全局答案用左右链相加更新。
- 标准答案:
class Solution:
def diameterOfBinaryTree(self, root):
ans = 0
def dfs(node):
nonlocal ans
if not node:
return 0
left = dfs(node.left)
right = dfs(node.right)
ans = max(ans, left + right)
return max(left, right) + 1
dfs(root)
return ans
- 小坑:返回的是节点高度,但答案是边数;
left + right正好是经过当前节点的边数。