列表

详情


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 。

 

提示:

原站题解

去查看

上次编辑到这里,代码来自缓存 点击恢复默认模板
class Solution { public: vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) { } };

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
}

上一题