【更新】自底向上包含自顶向下

题目描述

有一棵根节点为 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;
    }
};

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值