2003. 每棵子树内缺失的最小基因值
有一棵根节点为 0
的 家族树 ,总共包含 n
个节点,节点编号为 0
到 n - 1
。给你一个下标从 0 开始的整数数组 parents
,其中 parents[i]
是节点 i
的父节点。由于节点 0
是 根 ,所以 parents[0] == -1
。
总共有 105
个基因值,每个基因值都用 闭区间 [1, 105]
中的一个整数表示。给你一个下标从 0 开始的整数数组 nums
,其中 nums[i]
是节点 i
的基因值,且基因值 互不相同 。
请你返回一个数组 ans
,长度为 n
,其中 ans[i]
是以节点 i
为根的子树内 缺失 的 最小 基因值。
节点 x
为根的 子树 包含节点 x
和它所有的 后代 节点。
示例 1:
输入:parents = [-1,0,0,2], nums = [1,2,3,4] 输出:[5,1,1,1] 解释:每个子树答案计算结果如下: - 0:子树包含节点 [0,1,2,3] ,基因值分别为 [1,2,3,4] 。5 是缺失的最小基因值。 - 1:子树只包含节点 1 ,基因值为 2 。1 是缺失的最小基因值。 - 2:子树包含节点 [2,3] ,基因值分别为 [3,4] 。1 是缺失的最小基因值。 - 3:子树只包含节点 3 ,基因值为 4 。1是缺失的最小基因值。
示例 2:
输入:parents = [-1,0,1,0,3,3], nums = [5,4,6,2,1,3] 输出:[7,1,1,4,2,1] 解释:每个子树答案计算结果如下: - 0:子树内包含节点 [0,1,2,3,4,5] ,基因值分别为 [5,4,6,2,1,3] 。7 是缺失的最小基因值。 - 1:子树内包含节点 [1,2] ,基因值分别为 [4,6] 。 1 是缺失的最小基因值。 - 2:子树内只包含节点 2 ,基因值为 6 。1 是缺失的最小基因值。 - 3:子树内包含节点 [3,4,5] ,基因值分别为 [2,1,3] 。4 是缺失的最小基因值。 - 4:子树内只包含节点 4 ,基因值为 1 。2 是缺失的最小基因值。 - 5:子树内只包含节点 5 ,基因值为 3 。1 是缺失的最小基因值。
示例 3:
输入:parents = [-1,2,3,0,2,4,1], nums = [2,3,4,5,6,7,8] 输出:[1,1,1,1,1,1,1] 解释:所有子树都缺失基因值 1 。
提示:
n == parents.length == nums.length
2 <= n <= 105
i != 0
,满足 0 <= parents[i] <= n - 1
parents[0] == -1
parents
表示一棵合法的树。1 <= nums[i] <= 105
nums[i]
互不相同。原站题解
python3 解法, 执行用时: 344 ms, 内存消耗: 53.6 MB, 提交时间: 2023-10-31 00:06:50
class Solution: def smallestMissingValueSubtree(self, parents: List[int], nums: List[int]) -> List[int]: n = len(parents) ans = [1] * n if 1 not in nums: return ans g = defaultdict(list) for i, p in enumerate(parents): g[p].append(i) u, v, now = nums.index(1), nums.index(1), 1 vis = [False] * 100010 while u != -1: q = deque([w for w in g[u] if w != v]) vis[nums[u]] = True while q: tmp = q.popleft() vis[nums[tmp]] = True q.extend(g[tmp]) for i in range(now, 100010): if vis[i] == False: ans[u] = now = i break u, v = parents[u], u return ans
python3 解法, 执行用时: 492 ms, 内存消耗: 83.7 MB, 提交时间: 2023-10-10 15:30:18
class Solution: def smallestMissingValueSubtree(self, parents: List[int], nums: List[int]) -> List[int]: if 1 not in nums: return [1] * len(parents) d_path = defaultdict(list) for idx, val in enumerate(parents): if val >= 0: d_path[val].append(idx) for idx, num in enumerate(nums): if num == 1: break note = [0] * len(parents) visited = [0] * len(parents) def dfs(pt): if visited[pt] == 0: visited[pt] = 1 if nums[pt] - 1 < len(nums): note[nums[pt] - 1] = 1 for new_pt in d_path[pt]: dfs(new_pt) res = [1] * len(parents) tmp_pos = 0 while idx != -1: dfs(idx) while tmp_pos < len(nums) and note[tmp_pos] == 1: tmp_pos += 1 res[idx] = tmp_pos + 1 idx = parents[idx] return res
java 解法, 执行用时: 172 ms, 内存消耗: 88.7 MB, 提交时间: 2023-10-10 15:29:46
class Solution { int n; int[] parent; int[] num; boolean[] exist; int[] ans; int p = 1; Map<Integer, List<Integer>> edges; public int[] smallestMissingValueSubtree(int[] parents, int[] nums) { n = nums.length; parent = parents; num = nums; exist = new boolean[n + 2]; ans = new int[n]; edges = new HashMap<>(); for (int i = 0; i < n; i++) { edges.putIfAbsent(parents[i], new ArrayList<>()); edges.get(parents[i]).add(i); } // 标记完后,计算出不含1的子树的答案都是1,此时含1的子树答案还是0 dfsAbout1(0); // 计算含1的子树的答案是多少 dfsAns(0); return ans; } boolean dfsAbout1(int root) { List<Integer> children = edges.get(root); boolean res = false; if (children != null) { for (int child : children) { res |= dfsAbout1(child); } } if (num[root] == 1) res = true; if (!res) { ans[root] = 1; } return res; } void dfsAns(int root) { List<Integer> children = edges.get(root); if (children != null) { // 先递归处理含1的子树 for (int child : children) { if (ans[child] != 1) { dfsAns(child); } } // 处理不含1的子树,记录当前子树内含有哪些整数 for (int child : children) { if (ans[child] == 1) { dfsAdd(child); } } } // 记录当前子树根节点的整数 exist[num[root]] = true; // 查找当前缺失的第一个整数 while (exist[p]) { p++; } ans[root] = p; } void dfsAdd(int root) { exist[num[root]] = true; List<Integer> children = edges.get(root); if (children != null) { for (int child : children) { dfsAdd(child); } } } }
cpp 解法, 执行用时: 400 ms, 内存消耗: 154.5 MB, 提交时间: 2023-10-10 15:28:51
class Solution { public: vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) { int n = parents.size(); vector<int> son[n]; for (int i = 1; i < n; ++i) son[parents[i]].push_back(i); vector<int> mex(n, 1); // 找 1 int one = -1; for (int i = 0; i < n; ++i) { if (nums[i] == 1) { one = i; break; } } unordered_set<int> st; function<void(int)> dfs = [&](int u) -> void { st.insert(nums[u]); for (int v : son[u]) if (not st.count(nums[v])) dfs(v); }; int cur_mex = 1; while (one != -1) { dfs(one); while (true) { if (not st.count(cur_mex)) break; cur_mex++; } mex[one] = cur_mex; one = parents[one]; } return mex; } };
cpp 解法, 执行用时: 1016 ms, 内存消耗: 349.2 MB, 提交时间: 2023-10-10 15:28:32
class Solution { public: vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) { int n = parents.size(); vector<int> son[n]; for (int i = 1; i < n; ++i) son[parents[i]].push_back(i); vector<int> mex(n); function<unordered_set<int>(int)> dfs = [&](int u) -> unordered_set<int> { unordered_set<int> fa; mex[u] = 1; for (int v : son[u]) { auto s = std::move(dfs(v)); if (s.size() > fa.size()) swap(s, fa); for (int x : s) fa.insert(x); if (mex[v] > mex[u]) mex[u] = mex[v]; } fa.insert(nums[u]); while (true) { if (not fa.count(mex[u])) break; mex[u]++; } return fa; }; dfs(0); return mex; } };
golang 解法, 执行用时: 280 ms, 内存消耗: 20.2 MB, 提交时间: 2023-10-10 15:28:05
/** * 利用无重复基因值的性质 * 由于没有重复基因,若存在一个节点 x,其基因值为 1,那么从 x 到根这一条链上的所有节点, * 由于子树包含 x,其 mex均大于 1,而其余不在链上的节点,由于子树不包含 x,故其 mex 均为 1。 * 因此,我们只需要计算在这条链上的节点的 mex 值。 * 我们可以从 x 出发,顺着父节点往根走,同时收集当前子树下的所有基因值, * 然后再不断自增子树的 mex 直至其不在基因值集合中。 */ func smallestMissingValueSubtree(parents []int, nums []int) []int { n := len(parents) mex := make([]int, n) for i := range mex { mex[i] = 1 } g := make([][]int, n) for w := 1; w < n; w++ { v := parents[w] g[v] = append(g[v], w) } inSet := map[int]bool{} var f func(int) f = func(v int) { inSet[nums[v]] = true // 收集基因值 for _, w := range g[v] { if !inSet[nums[w]] { // 避免重复访问节点 f(w) } } } // 找到基因值等于 1 的节点 x x := -1 for i, v := range nums { if v == 1 { x = i break } } // x 顺着父节点往上走 for cur := 2; x >= 0; x = parents[x] { f(x) for inSet[cur] { cur++ // 不断自增直至不在基因值集合中 } mex[x] = cur } return mex }
golang 解法, 执行用时: 404 ms, 内存消耗: 80.4 MB, 提交时间: 2023-10-10 15:26:38
/** * 启发式合并 * 遍历整棵树,统计每棵子树包含的基因值集合,以及缺失的最小基因值(记作 mex)。 * 合并基因值集合时,总是从小的往大的合并(类似并查集的按秩合并), * 同时更新当前子树的 mex 的最大值。合并完成后再不断自增子树的 mex 直至其不在基因值集合中。 * 这一方法同时也适用于有相同基因值的情况。 */ func smallestMissingValueSubtree(parents []int, nums []int) []int { n := len(parents) g := make([][]int, n) for w := 1; w < n; w++ { v := parents[w] g[v] = append(g[v], w) } mex := make([]int, n) var f func(int) map[int]bool f = func(v int) map[int]bool { inSet := map[int]bool{} mex[v] = 1 for _, w := range g[v] { s := f(w) // 启发式合并:保证从小的集合合并到大的集合 if len(s) > len(inSet) { inSet, s = s, inSet } for x := range s { inSet[x] = true } if mex[w] > mex[v] { mex[v] = mex[w] } } inSet[nums[v]] = true for inSet[mex[v]] { mex[v]++ // 不断自增直至 mex[v] 不在集合中 } return inSet } f(0) return mex }