注:由于周赛中的链表题可以转成数组处理,难度比直接处理链表低,故不标明难度分。
带着问题去做下面的题目:
while (node != null)
?什么情况下要写 while (node.next != null)
?思维扩展:
学习递归,从二叉树开始。
晕递归的同学,请先看视频讲解【基础算法精讲 09】,欢迎点赞~
带着问题去做下面的题目:
在「递」的过程中维护值。
有些题目自顶向下和自底向上都可以做。有些题目也可以用 BFS 做。
在「归」的过程中计算。
另见本题单的「§3.5 树的直径」。
更多树形 DP,见 动态规划题单 中的「树形 DP」。
带权树 LCA 模板(节点编号从 0 开始):
Python3
Java
C++
Go
class LcaBinaryLifting:
def __init__(self, edges: List[List[int]]):
n = len(edges) + 1
self.m = m = n.bit_length()
g = [[] for _ in range(n)]
for x, y, w in edges:
g[x].append((y, w))
g[y].append((x, w))
depth = [0] * n
dis = [0] * n # 如果是无权树(边权为 1),dis 可以去掉,用 depth 代替
pa = [[-1] * m for _ in range(n)]
def dfs(x: int, fa: int) -> None:
pa[x][0] = fa
for y, w in g[x]:
if y != fa:
depth[y] = depth[x] + 1
dis[y] = dis[x] + w
dfs(y, x)
dfs(0, -1)
for i in range(m - 1):
for x in range(n):
if (p := pa[x][i]) != -1:
pa[x][i + 1] = pa[p][i]
self.depth = depth
self.dis = dis
self.pa = pa
def get_kth_ancestor(self, node: int, k: int) -> int:
for i in range(k.bit_length()):
if k >> i & 1:
node = self.pa[node][i]
return node
# 返回 x 和 y 的最近公共祖先
def get_lca(self, x: int, y: int) -> int:
if self.depth[x] > self.depth[y]:
x, y = y, x
# 使 y 和 x 在同一深度
y = self.get_kth_ancestor(y, self.depth[y] - self.depth[x])
if y == x:
return x
for i in range(self.m - 1, -1, -1):
px, py = self.pa[x][i], self.pa[y][i]
if px != py:
x, y = px, py # 同时往上跳 2**i 步
return self.pa[x][0]
# 返回 x 到 y 的距离(最短路长度)
def get_dis(self, x: int, y: int) -> int:
return self.dis[x] + self.dis[y] - self.dis[self.get_lca(x, y)] * 2
数组上的倍增:
另见 动态规划题单 中的「树形 DP」。
本质是搜索树上的 DFS。
推荐先完成 §2.7 节。先理解二叉树上的回溯,再来学习一般情况下的回溯。
有「选或不选」和「枚举选哪个」两种写法。
也可以用二进制枚举做。
思维扩展:
把分割线(逗号)看成是可以「选或不选」的东西,本质是子集型回溯。
有个数上的约束。也算作子集型回溯。
部分题目也可以用状压 DP 做。
英文名 meet in the middle。
讲解(见文末)
欢迎关注 B站@灵茶山艾府
如果你发现有题目可以补充进来,欢迎评论反馈。