时间戳——树中的应用
简介
参考来源:Leetcode299周赛T4题解
时间戳:节点在dfs被遍历到的开始以及结束时间。
具体地讲,我们可以在 DFS 一棵树的过程中,维护一个全局的时间戳 clock,每访问一个新的节点,就将 clock +1。同时,记录进入节点 x 时的时间戳 in[x],和离开(递归结束)这个节点时的时间戳 out[x]。
作用
判断两个节点是否存在父子关系。
根据 DFS 的性质,当我们递归以 x 为根的子树时,设 y 是 x 的子孙节点,我们必须先递归完以 y 为根的子树,之后才能递归完以 x 为根的子树。
从时间戳上看,如果 y 是 x 的子孙节点,那么区间 [in[y],out[y]] 必然被区间 [in[x],out[x]] 所包含。
反之,如果区间 [in[y],out[y]] 被区间 [in[x],out[x]] 所包含,那么 y 必然是 x 的子孙节点(换句话说 x 是 y 的祖先节点)。因此我们可以通过
\[\textit{in}[x]<\textit{in}[y]\le\textit{out}[y]\le\textit{out}[x]
\]
来判断 x 是否为 y 的祖先节点,由于 in[y]≤out[y] 恒成立,上式可以简化为
\[\textit{in}[x]<\textit{in}[y]\le\textit{out}[x]
\]
复杂度分析
时间复杂度:O(n^2)
空间复杂度:O(n)
示例
from itertools import combinations
from math import inf
from typing import *
#DFS时间戳
class Solution:
def minimumScore(self, nums: List[int], edges: List[List[int]]) -> int:
n = len(nums)
g = [[] for _ in range(n)]
for x, y in edges:
g[x].append(y)
g[y].append(x)
xor, in_, out, clock = [0] * n, [0] * n, [0] * n, 0
def dfs(x: int, fa: int) -> None:
nonlocal clock
clock += 1
in_[x] = clock
xor[x] = nums[x]
for y in g[x]:
if y != fa:
dfs(y, x)
xor[x] ^= xor[y]
out[x] = clock
dfs(0, -1)
ans = inf
for i in range(2, n):
for j in range(1, i):
if in_[i] < in_[j] <= out[i]: # i 是 j 的祖先节点
x, y, z = xor[j], xor[i] ^ xor[j], xor[0] ^ xor[i]
elif in_[j] < in_[i] <= out[j]: # j 是 i 的祖先节点
x, y, z = xor[i], xor[i] ^ xor[j], xor[0] ^ xor[j]
else: # 删除的两条边分别属于两颗不相交的子树
x, y, z = xor[i], xor[j], xor[0] ^ xor[i] ^ xor[j]
ans = min(ans, max(x, y, z) - min(x, y, z))
if ans == 0: return 0 # 提前退出
return ans
s=Solution()
nums = [1,5,5,4,11]
edges = [[0,1],[1,2],[1,3],[3,4]]
z=s.minimumScore(nums,edges)
print(z)