题目描述
有一棵根节点为 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 和它所有的 后代 节点。
输入: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 是缺失的最小基因值
刚开始尝试的代码如下:
class Solution {
public:
vector<int> t;
vector<int> dfs(int ver,vector<int>vis,int i,map<int,vector<int>>& mp,vector<int>& nums){
if(i!=mp[-1][0]&&vis[nums[i]]==1) return vis;
if(mp[i].empty()){ //对根节点的处理
vis[nums[i]]=1;
if(ver==1) //只有遍历子树自己而不是用来合并思路时会将其赋值
t[i]= distance(vis.begin(),find(vis.begin(), vis.end(), 0)); //处理包含自己之外的
return vis;
}
vector<int> vis2=vis,vis3=vis;
for(auto &t:mp[i]) { //不同分支之间会互相影响(尝试传值而不是传递引用)
if(ver!=0)
dfs(1,vis3,t,mp,nums); //返不返回无所谓(每次传入的是同一个值)
vis2=dfs(0,vis2,t,mp,nums); //用来一直叠加的
}
vis2[nums[i]]=1;
if(ver==1)
t[i]= distance(vis2.begin(),find(vis2.begin(), vis2.end(), 0));
return vis2;
}
vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
vector<int> vis;
vis.resize(*max_element(nums.begin(),nums.end())+1); //最大的那个基因值作为参考即可
fill(vis.begin(),vis.end(),0);
t.resize(nums.size());
map<int,vector<int>> mp;
for(auto i=0;i<parents.size();i++)
mp[parents[i]].push_back(i); //而且这是自动排序的
vis[0]=1;
dfs(1,vis,mp[-1][0],mp,nums);
return t;
}
};
错误就是超时,分析如下
对超时的猜想:
distance和find函数找第一个没标记的值,这里估计有更好的算法,但是数据范围是10^5,二分法?肯定不可行,因为值只有0和1,并不是有序的
dfs本身是没问题的,这里还加入了剪枝,所以上面那个问题还待解决~
自底向上的思路分析
-
自底向上遍历,不会出现不同结点之间相互影响的问题
-
自底向上中有含有自顶向下
-
“if (flag[x] == false)”
防止向下dfs过的再次dfs
-
-
注意“
while (vis[idx]) ++idx;
”,不必每次都从1开始往后查找(有后效性),因为是自底向上的 -
全部代码如下
class Solution {
public:
vector<int> t;
vector<bool> flag;
vector<int> nums1;
vector<int> vis; //基因值访问情况
int idx = 1; // 记录基因值1这条路径上的缺失基因
map<int,vector<int>> mp;
void downDfs(int now) // 自顶向下DFS
{
vis[nums1[now]] = true;
for (auto &x : mp[now])
downDfs(x);
return;
}
void upDfs(vector<int>& parents, int now) // 自底向上DFS
{
if (now == -1)
return;
// 找到所有不在当前基因值1路径的子节点,往下DFS
for (auto &x : mp[now])
if (flag[x] == false) //防止向下dfs过的再次dfs
downDfs(x);
// 在已有的基因序列中枚举出第一个不存在的基因
vis[nums1[now]] = 1;
while (vis[idx]) ++idx; //不必每次都从1开始往后查找(有后效性)
t[now] = idx;
// 自底向上DFS
upDfs(parents, parents[now]);
return;
}
vector<int> smallestMissingValueSubtree(vector<int>& parents, vector<int>& nums) {
nums1=nums;
vis.resize(*max_element(nums.begin(),nums.end())+2,0); //最大的那个基因值作为参考即可
vis[0]=1;
t.resize(nums.size(),1); //设置的默认值都为1
for(auto i=0;i<parents.size();i++)
mp[parents[i]].push_back(i); //而且这是自动排序的
//找到所有在基因值为1那条路径的下标
flag.resize(nums.size(), false);
auto end=find(nums.begin(),nums.end(),1);
if(end!=nums.end()){
int pos= distance(nums.begin(),find(nums.begin(),nums.end(),1));
for (int j = pos; j != -1; j = parents[j])
flag[j]=true;
upDfs(parents,pos);
}
return t;
}
};