在算法竞赛和实际工程中,Python树上倍增是一种非常高效且常用的技术,尤其适用于处理树结构算法中的查询问题。本文将从零开始,手把手教你理解并实现倍增法求最近公共祖先(LCA)这一经典应用,即使你是编程小白,也能轻松掌握!
在一棵有根树中,任意两个节点都存在一个“最近”的共同祖先节点,这个节点就是它们的最近公共祖先(Lowest Common Ancestor, 简称 LCA)。例如,在下图中,节点 4 和 5 的 LCA 是节点 2。

最朴素的方法是让两个节点同时向上跳,直到相遇。但当树很深时(比如 10⁵ 层),每次查询可能需要 O(n) 时间,效率太低。
而树上倍增的核心思想是:预处理每个节点向上跳 2^k 步能到达的位置。这样在查询时,我们可以像二进制一样“跳跃式”上升,将单次查询时间优化到 O(log n)!
我们定义 up[u][k] 表示节点 u 向上跳 2^k 步后到达的祖先节点。
预处理时,利用动态规划思想:
up[u][0] = parent[u](跳 1 步就是直接父节点)up[u][k] = up[ up[u][k-1] ][k-1](跳 2^k 步 = 先跳 2^{k-1} 步,再跳 2^{k-1} 步)查询 LCA(a, b) 的步骤:
up[a][k] != up[b][k],就同时往上跳;下面是一个完整的、可运行的 Python 示例,包含建树、预处理和 LCA 查询:
import mathfrom collections import defaultdict, deque# 构建邻接表def build_tree(edges): graph = defaultdict(list) for u, v in edges: graph[u].append(v) graph[v].append(u) # 无向图 return graph# DFS 预处理深度和父节点def dfs(root, graph, parent, depth): stack = [(root, -1, 0)] # (当前节点, 父节点, 深度) while stack: u, p, d = stack.pop() parent[u] = p depth[u] = d for v in graph[u]: if v != p: stack.append((v, u, d + 1))# 主函数:LCA 类class LCA: def __init__(self, edges, root=0): self.graph = build_tree(edges) self.n = len(set([u for e in edges for u in e])) self.root = root # 初始化深度和直接父节点 self.depth = [0] * self.n self.parent0 = [-1] * self.n dfs(root, self.graph, self.parent0, self.depth) # 计算最大倍增层数 self.LOG = math.floor(math.log2(self.n)) + 1 # 初始化 up 表 self.up = [[-1] * self.LOG for _ in range(self.n)] # 第0层就是直接父节点 for i in range(self.n): self.up[i][0] = self.parent0[i] # 动态规划填充 up 表 for j in range(1, self.LOG): for i in range(self.n): if self.up[i][j-1] != -1: self.up[i][j] = self.up[self.up[i][j-1]][j-1] def query(self, a, b): # 确保 a 是更深的节点 if self.depth[a] < self.depth[b]: a, b = b, a # 将 a 提升到与 b 同一深度 diff = self.depth[a] - self.depth[b] bit = 0 while diff: if diff & 1: a = self.up[a][bit] if a == -1: # 超出根节点 break diff >>= 1 bit += 1 if a == b: return a # 从高到低尝试跳跃 for j in range(self.LOG - 1, -1, -1): if self.up[a][j] != self.up[b][j]: a = self.up[a][j] b = self.up[b][j] return self.up[a][0] # 返回父节点即 LCA# 使用示例if __name__ == "__main__": # 构建如下树: # 0 # / \ # 1 2 # / \ \ # 3 4 5 edges = [(0,1), (0,2), (1,3), (1,4), (2,5)] lca_solver = LCA(edges, root=0) print("LCA of 3 and 4 is:", lca_solver.query(3, 4)) # 输出: 1 print("LCA of 4 and 5 is:", lca_solver.query(4, 5)) # 输出: 0 print("LCA of 3 and 5 is:", lca_solver.query(3, 5)) # 输出: 0通过本文,你已经掌握了Python树上倍增的基本思想和实现方法。这项技术不仅能用于LCA算法,还可扩展到路径最大值、路径和等更复杂的问题。
记住关键点:
up[u][k] 表,利用倍增思想;现在,你已经具备了使用倍增法求最近公共祖先的能力!快去刷题巩固吧~
本文由主机测评网于2025-12-19发表在主机测评网_免费VPS_免费云服务器_免费独立服务器,如有疑问,请联系我们。
本文链接:https://www.vpshk.cn/20251210042.html