1. 题意
给定一颗树,求所有子树的最小基因值。
最小基因值定义为,该树的所有节点组成的集合中未出现的最小正整数。
2. 题解
2.1 启发式合并
直接递归求出左右子树的所有可能值,与根节点进行合并,注意始终是大集合合并小集合。即启发式合并。
- 一个小优化,父节点的最小基因值必定大于等于子节点
class Solution {
public:
vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
int sz = parents.size();
vector<int> res(sz, 1);
vector<vector<int>> childs(sz);
int kNode = -1;
for ( int i = 0; i < sz; ++i) {
if ( parents[i] >= 0)
childs[parents[i]].push_back(i);
if ( nums[i] == 1)
kNode = i;
}
vector<int> vis(sz, 0);
unordered_set<int> um;
function<void(int)> dfs =
[&](int rt) {
if (vis[rt])
return;
vis[rt] = 1;
um.insert(nums[rt]);
for (int v:childs[rt])
dfs(v);
};
int cnt = 1;
while ( kNode != -1) {
dfs(kNode);
while ( um.count(cnt))
++cnt;
res[kNode] = cnt;
kNode = parents[kNode];
}
return res;
}
};
2.2 找出值为1的节点
实际上不难得出,对于所有子节点不包含 1
的树,他们的最小基因值为1
。
而对于以1
为祖先节点的子节点来说,他们的最小基因值也为1
。
只需要考虑值为1
的祖先节点的最小基因值;所以我们可以先找到值为1
的节点,递归求出他的所有
子节点值集合,自底向上求出祖先,我们可以vis
标记这个节点是否被求过来减少重复。
class Solution {
public:
vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
int sz = parents.size();
vector<int> res(sz, 1);
vector<vector<int>> childs(sz);
int kNode = -1;
for ( int i = 0; i < sz; ++i) {
if ( parents[i] >= 0)
childs[parents[i]].push_back(i);
if ( nums[i] == 1)
kNode = i;
}
vector<int> vis(sz, 0);
unordered_set<int> um;
function<void(int)> dfs =
[&](int rt) {
if (vis[rt])
return;
vis[rt] = 1;
um.insert(nums[rt]);
for (int v:childs[rt])
dfs(v);
};
int cnt = 1;
while ( kNode != -1) {
dfs(kNode);
while ( um.count(cnt))
++cnt;
res[kNode] = cnt;
kNode = parents[kNode];
}
return res;
}
};