二叉树
(1)104. 二叉树的最大深度

方法1:二叉树的最大深度 = max(左子树的深度,右子树的深度) + 1
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
if root is None:
return 0
max_l = self.maxDepth(root.left)
max_r = self.maxDepth(root.right)
return max(max_l, max_r) + 1
方法2:递归遍历树,每走一步深度+1
class Solution:
def maxDepth(self, root: Optional[TreeNode]) -> int:
ans = 0
def f(node, cnt):
if node is None:
return
cnt += 1
nonlocal ans
ans = max(ans, cnt)
f(node.left, cnt)
f(node.right, cnt)
f(root, 0)
return ans
(2)100.相同的树

判断两棵树是否相同?
- 首先递归判断左子树是否相同
- 然后再递归判断右子树是否相同
class Solution:
def isSameTree(self, p: Optional[TreeNode], q: Optional[TreeNode]) -> bool:
# 只有p,q同时为none时, 才说明两棵树是相同的
if p is None or q is None:
return p == q
if p.val != q.val:
return False
return self.isSameTree(p.left, q.left) and self.isSameTree(p.right, q.right)
(3)101.对称二叉树
结合<相同的树>的代码,判断左右子树是否相同:
- 左子树的左子树 == 右子树的右子树
- 左子树的右子树 == 右子树的左子树
class Solution:
def isSameTree(self, p, q):
if p is None or q is None:
return p == q
if p.val != q.val:
return False
return self.isSameTree(p.left, q.right) and self.isSameTree(p.right, q.left)
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
return self.isSameTree(root.left, root.right)
(4)110.平衡二叉树
平衡二叉树 --> 左右子树的高度差不超过1

递归计算左右子树的高度, 最终判断左右子数的高度差
class Solution:
def isBalanced(self, root: Optional[TreeNode]) -> bool:
def get_height(node):
if node is None:
return 0
left_height = get_height(node.left)
if left_height == -1:
return -1
right_height = get_height(node.right)
if right_height == -1 or abs(left_height-right_height) > 1:
return -1
return max(left_height, right_height) + 1
return get_height(root) != -1
(5)199.二叉树的右视图

只有当 深度 == 当前答案的长度
时,答案才会被记录
class Solution:
def rightSideView(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def f(node, depth):
if node is None:
return
if len(ans) == depth:
ans.append(node.val)
f(node.right, depth+1)
f(node.left, depth+1)
f(root, 0)
return ans
(6)226. 翻转二叉树

class Solution:
def invertTree(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
if root is None:
return None
left = self.invertTree(root.left)
right = self.invertTree(root.right)
root.left = right
root.right = left
return root
(7)108. 将有序数组转换为二叉搜索树

class Solution:
def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
if not nums:
return None
mid = len(nums) // 2
left = self.sortedArrayToBST(nums[:mid])
right = self.sortedArrayToBST(nums[mid+1:])
return TreeNode(nums[mid], left, right)
二叉树的遍历
(1)94. 二叉树的中序遍历
二叉树的前中后序遍历(前中后表示的是根节点的遍历顺序)
- 前序遍历(Pre-order Traversal):根,左,右
- 中序遍历(In-order Traversal):左,根,右
- 后序遍历(Post-order Traversal):左,右,根
前序遍历:ABDECFG
中序遍历:DBEAFCG
后序遍历:DEBFGCA
方法一:递归写法
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
ans = []
def f(node):
if node is None:
return
f(node.left)
ans.append(node.val)
f(node.right)
f(root)
return ans
方法2:迭代
class Solution:
def inorderTraversal(self, root: Optional[TreeNode]) -> List[int]:
white, gray = 0, 1 # white表示未被访问, gray表示被访问
ans = []
stack = [(white, root)]
while stack:
color, node = stack.pop()
if node is None:
continue
if color == white:
# 因为要保证出栈的时候是 左-->根-->右
# 所以入栈的时候要是 右-->根-->左
stack.append((white, node.right))
stack.append((gray, node))
stack.append((white, node.left))
else:
ans.append(node.val)
return ans
(2)98. 验证二叉搜索树


方法1:前序遍历
root.left
的值域应该<root.val
,更新区间(left, root.val)
root.right
的值域应该>root.val
,更新区间(root.val, right)

class Solution:
def check(self, node, left, right):
if node is None:
return True
x = node.val
return left < x < right and self.check(node.left, left, x) and self.check(node.right, x, right)
def isValidBST(self, root: Optional[TreeNode]) -> bool:
return self.check(root, -inf, inf)
方法2:中序遍历
按照中序遍历的顺序,一个二叉搜索树,应该满足下一个结点比上一个结点大
class Solution:
pre = -inf
def isValidBST(self, root: Optional[TreeNode]) -> bool:
if root is None:
return True
l = self.isValidBST(root.left)
if root.val <= self.pre:
return False
self.pre = root.val
r = self.isValidBST(root.right)
return l and r
方法3:后序遍历
class Solution:
def isValidBST(self, root: Optional[TreeNode]) -> bool:
def f(node):
if node is None:
return inf, -inf
l_min, l_max = f(node.left)
r_min, r_max = f(node.right)
x = node.val
if x <= l_max or x >= r_min:
return -inf, inf
return min(l_min, x), max(r_max, x)
return f(root)[0] != -inf
(3)230. 二叉搜索树中第 K 小的元素
中序遍历二叉搜索树,得到的是从小到大排序的序列,取第k个元素即可
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
ans = []
def f(node):
if node is None:
return None
f(node.left)
ans.append(node.val)
f(node.right)
f(root)
return ans[k-1]
(4)114.展开二叉树为链表
写法一
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
ls = []
def dfs(node):
if not node:
return
ls.append(node)
dfs(node.left)
dfs(node.right)
dfs(root)
n = len(ls)
if n > 0:
head = ls[0]
for node in ls[1:]:
head.left = None
head.right = node
head = head.right
写法二
class Solution:
def flatten(self, root: Optional[TreeNode]) -> None:
"""
Do not return anything, modify root in-place instead.
"""
ans = []
def dfs(node):
if node is None:
return
ans.append(node)
dfs(node.left)
dfs(node.right)
dfs(root)
n = len(ans)
for i in range(1, n):
pre, cur = ans[i-1], ans[i]
pre.left = None
pre.right = cur
(5)105. 从前序与中序遍历序列构造二叉树
- 前序找根(根,左,右)
- 中序来分(左,根,右)
- 根据前序找到根节点,在根据中序,找到根节点的左右子结点
(同理,后序+中序也是一样的思路,在后序找根,中序来分)
步骤
- 前序数组为空时,空结点(相当于没有根节点了)
- 前序数组的第一个元素为 根节点
- 找到根节点后,在中序数组中切分,找到根节点的左右子树
- 切分中序数组,得到左右区间
- 切分前序数组,得到左右区间
- 递归处理左右区间
class Solution:
def buildTree(self, preorder: List[int], inorder: List[int]) -> TreeNode:
# 第一步: 特殊情况讨论: 树为空. 或者说是递归终止条件
if not preorder:
return None
# 第二步: 前序遍历的第一个就是当前的中间节点.
root_val = preorder[0]
root = TreeNode(root_val)
# 第三步: 找切割点.
separator_idx = inorder.index(root_val)
# 第四步: 切割inorder数组. 得到inorder数组的左,右半边.
inorder_left = inorder[:separator_idx]
inorder_right = inorder[separator_idx + 1:]
# 第五步: 切割preorder数组. 得到preorder数组的左,右半边.
preorder_left = preorder[1:1 + len(inorder_left)]
preorder_right = preorder[1 + len(inorder_left):]
# 第六步: 递归
root.left = self.buildTree(preorder_left, inorder_left)
root.right = self.buildTree(preorder_right, inorder_right)
# 第七步: 返回答案
return root
(6)106. 从中序与后序遍历序列构造二叉树
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def buildTree(self, inorder: List[int], postorder: List[int]) -> Optional[TreeNode]:
# 为空返回
if not postorder:
return
# 1.找根
root_val = postorder[-1]
root = TreeNode(val=root_val)
# 2.中序划分左右区间
inorder_idx = inorder.index(root_val)
inorder_left = inorder[:inorder_idx]
inorder_right = inorder[inorder_idx+1:]
# 3.后续划分左右
postorder_left = postorder[:len(inorder_left)]
postorder_right = postorder[len(inorder_left):-1]
root.left = self.buildTree(inorder_left, postorder_left)
root.right = self.buildTree(inorder_right, postorder_right)
return root
二叉树的最近公共祖先

结点的祖先(根节点)
- 对于结点6,它的祖先有
6,5,3
(包括自己本身) - 对于结点4,它的祖先有
4,2,5,3
结点的公共祖先
- 结点6和4的公共祖先,
5,3
- 最近公共祖先
5
(1)236. 二叉树的最近公共祖先


(1)找到 p 或 q 后,无需继续遍历后续结点——>返回当前结点
例如找到 p = 5
,无论 q
为 5
后续的哪一个结点,pq的最近公共祖先都是5
(2)如果在左右子树分别找到了p
和q
——>返回当前结点
例如找到 p = 5
,q = 1
,则最近公共祖先就是当前结点3
(3)如果只在其中一个子树中找到了p
和q
——>返回左(或右)子树找到的结果
例如,在左子树中找到了p=5
和q=4
class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
if root is None or root is p or root is q:
return root
left = self.lowestCommonAncestor(root.left, p, q)
right = self.lowestCommonAncestor(root.right, p, q)
if left and right:
return root
if left:
return left
if right:
return right
(2)235. 二叉搜索树的最近公共祖先


class Solution:
def lowestCommonAncestor(self, root: 'TreeNode', p: 'TreeNode', q: 'TreeNode') -> 'TreeNode':
x = root.val
if p.val < x and q.val < x: # 都在左子树
return self.lowestCommonAncestor(root.left, p, q)
if p.val > x and q.val > x: # 都在右子树
return self.lowestCommonAncestor(root.right, p, q)
return root
二叉树的层次遍历
(1)102. 二叉树的层序遍历


- 用队列
- 队列初始时要加括号,
q = deque([root])
,可迭代的 list 才能被初始化为 deque - 出队时记录值,并将其 左右 结点添加到队列中
from collections import deque
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
ans = []
q = deque([root])
while q:
vals = []
for _ in range(len(q)):
node = q.popleft()
vals.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
ans.append(vals)
return ans
不用队列
class Solution:
def levelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
ans = []
cur = [root]
while cur:
nxt = []
vals = []
for node in cur:
vals.append(node.val)
if node.left:
nxt.append(node.left)
if node.right:
nxt.append(node.right)
ans.append(vals)
cur = nxt
return ans
(2)103. 二叉树的锯齿形层序遍历

第一层,从左往右
第二次,从右往左
……
from collections import deque
class Solution:
def zigzagLevelOrder(self, root: Optional[TreeNode]) -> List[List[int]]:
if root is None:
return []
ans = []
q = deque([root])
layer = 1
while q:
vals = []
for _ in range(len(q)):
node = q.popleft()
vals.append(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
if layer % 2 > 0:
ans.append(vals)
else:
ans.append(vals[::-1])
layer += 1
return ans
(3)513. 找树左下角的值

求最后一层最左边的结点
方法一:
层次遍历,并保存每一层的第一个结点,最终得到最后一层的第一个结点
from collections import deque
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
q = deque([root])
ans = None
while q:
for i in range(len(q)):
node = q.popleft()
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
if i == 0:
ans = node.val
return ans
方法二:
修改层次遍历的顺序,修改为从右往左
,这样子最左边的结点就是队列中的最后一个结点
from collections import deque
class Solution:
def findBottomLeftValue(self, root: Optional[TreeNode]) -> int:
q = deque([root])
ans = None
while q:
node = q.popleft()
if node.right:
q.append(node.right)
if node.left:
q.append(node.left)
return node.val
路径总和
(1)112. 路径总和
class Solution:
def hasPathSum(self, root: Optional[TreeNode], targetSum: int) -> bool:
if root is None:
return False
def dfs(node, count): # count 表示剩余的和
if not node.left and not node.right: # 当前是叶子结点
if count == 0: # 当前路径满足条件
return True
else:
return False
if node.left:
count -= node.left.val
if dfs(node.left, count):
return True
count += node.left.val
if node.right:
count -= node.right.val
if dfs(node.right, count):
return True
count += node.right.val
return False
return dfs(root, targetSum - root.val)
(2)113. 路径总和 II
注意初始时要,path.append(root.val)
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> List[List[int]]:
if not root:
return []
ans = []
path = []
def dfs(node, count):
if not node.left and not node.right:
if count == 0:
ans.append(path.copy())
return
if node.left:
count -= node.left.val
path.append(node.left.val)
dfs(node.left, count)
count += node.left.val
path.pop()
if node.right:
count -= node.right.val
path.append(node.right.val)
dfs(node.right, count)
count += node.right.val
path.pop()
path.append(root.val)
dfs(root, targetSum-root.val)
return ans
(3)437. 路径总和 III
类似<560. 和为 K 的子数组>的解法:
- 二叉树的前缀和 = 根节点到当前结点的路径和
from collections import defaultdict
class Solution:
def pathSum(self, root: Optional[TreeNode], targetSum: int) -> int:
ans = 0
cnt = defaultdict(int)
cnt[0] = 1
# s 从根节点到当前结点的总和
def dfs(node: Optional[TreeNode], s: int) -> None:
if node is None:
return
nonlocal ans
s += node.val
tar_pre = s - targetSum
if tar_pre in cnt:
ans += cnt[tar_pre]
cnt[s] += 1
dfs(node.left, s)
dfs(node.right, s)
cnt[s] -= 1 # 恢复现场
dfs(root, 0)
return ans
树形DP
(1)543. 二叉树的直径
最长直径的起点和终点一定都是叶子结点
假设直径会经过某个点(例如上图中的 node 1)
- 那么对于当前结点,对应的最大直径为:
左子树的最大深度+右子树最大深度+2
,(2
表示左右子树到 node 1 的路径长度) - 而
深度 == max(左子树, 右子树) + 1
,所以其实相当于是在求最大深度的过程中,更新最大直径
注意,if node is None:
return -1
class Solution:
def diameterOfBinaryTree(self, root: TreeNode) -> int:
ans = 0
def dfs(node):
if node is None:
return -1
left = dfs(node.left)
right = dfs(node.right)
nonlocal ans
ans = max(ans, left + right + 2)
return max(left, right) + 1
dfs(root)
return ans
(2)124. 二叉树中的最大路径和
同样对于每个结点,计算它左右子树的最大链和
- 当前拐点的最大链和 = 左 + 右 + 当前拐点值
- 注意:当左或右为负数时,可以返回0,表示不经过该链
所以其实是求最大带权值的过程中,更新答案
class Solution:
def maxPathSum(self, root: Optional[TreeNode]) -> int:
ans = -inf
def dfs(node):
if node is None:
return 0
left = max(0, dfs(node.left))
right = max(0, dfs(node.right))
nonlocal ans
ans = max(ans, left + right + node.val)
return max(left, right) + node.val
dfs(root)
return ans