力扣每日一题 685. 冗余连接 II

在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。

输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1n)的树及一条附加的有向边构成。附加的边包含在 1n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 uivi 的一个父节点。

返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

简单来说就是有n个节点n条边的有向图,需要找到最后加入的一条删除后可以构成有向树的边。

今天这题目比昨天的难不少,或者说复杂不少,因为需要分情况考虑了,昨天的代码直接贴过来都会有测试用例过不去。

显然,一棵由有向图构成的树在增加一条边之后,会有两种可能。第一种情况,新增的边连接了根节点,不存在入度为0的根节点了。第二种情况,新增的边连接的两个非根节点,出现了一个入度为2的节点。

对于第一种情况,和昨天的做法是一样的,一条条加边,找到一条连接的两个点在同一个联通分量的边就可以返回了。

但对于第二种情况,我们需要找到那两条指向了同一个节点的边,然后尝试删除一条边,判断是否构成树。

cpp 复制代码
class Solution {
public:
    int find(int x)
    {
        if (tree[x] == x) return x;
        return find(tree[x]);
    }
    void unite(int x, int y)
    {
        int fx = find(x), fy = find(y);
        tree[fy] = fx;
    }
    bool isTree(vector<vector<int>>& edges, int ii)
    {
        const int n = edges.size();
        tree.resize(n+1);
        for (int i=1; i<=n; ++i) tree[i] = i;
        for (int i=0; i<n; ++i)
        {
            if (i == ii) continue;
            if (find(edges[i][0]) != find(edges[i][1]))
            {
                unite(edges[i][0], edges[i][1]);
            }
            else return false;
        }
        return true;
    }
    vector<int> findRedundantDirectedConnection(vector<vector<int>>& edges) {
        const int n = edges.size();
        int inDgree[n+1]; //记录入度
        int multi = 0;
        for (int i=1; i<=n; ++i) inDgree[i]=0;
        for (auto i:edges)
        {
            inDgree[i[1]]++;
            if (inDgree[i[1]] == 2) multi = i[1]; // 找到入度为2的点,保证最多只有一个
        }
        if (multi != 0) // 如果存在入度为2的点
        {
            for (int i=n-1; i>=0; --i)
            {
                if (edges[i][1] == multi) // 从后往前尝试删除指向的边
                {
                    if (isTree(edges, i)) // 如果后加入的边删除后构成树,那么就直接返回这条边即可
                    {
                        return edges[i];
                    }
                    else // 如果后加入的删除后无法构成树,那要删除的一定是先加入的那条边
                    {
                        while (--i)
                        {
                            if (edges[i][1] == multi)
                            {
                                return edges[i];
                            }
                        }
                    }
                }
            }
        }
        else // 没有入度为2的点,处理方法与昨天一致
        {
            tree.resize(n+1);
            for (int i=1; i<=n; ++i) tree[i] = i;
            for (int i=0; i<n; ++i)
            {
                if (find(edges[i][0]) != find(edges[i][1]))
                {
                    unite(edges[i][0], edges[i][1]);
                }
                else return edges[i];
            }
        }
        return edges[0];
    }
private:
    vector<int> tree;
};
相关推荐
草履虫建模37 分钟前
力扣算法 1768. 交替合并字符串
java·开发语言·算法·leetcode·职场和发展·idea·基础
naruto_lnq3 小时前
分布式系统安全通信
开发语言·c++·算法
Jasmine_llq3 小时前
《P3157 [CQOI2011] 动态逆序对》
算法·cdq 分治·动态问题静态化+双向偏序统计·树状数组(高效统计元素大小关系·排序算法(预处理偏序和时间戳)·前缀和(合并单个贡献为总逆序对·动态问题静态化
爱吃rabbit的mq4 小时前
第09章:随机森林:集成学习的威力
算法·随机森林·集成学习
(❁´◡`❁)Jimmy(❁´◡`❁)4 小时前
Exgcd 学习笔记
笔记·学习·算法
YYuCChi5 小时前
代码随想录算法训练营第三十七天 | 52.携带研究材料(卡码网)、518.零钱兑换||、377.组合总和IV、57.爬楼梯(卡码网)
算法·动态规划
不能隔夜的咖喱5 小时前
牛客网刷题(2)
java·开发语言·算法
VT.馒头5 小时前
【力扣】2721. 并行执行异步函数
前端·javascript·算法·leetcode·typescript
进击的小头5 小时前
实战案例:51单片机低功耗场景下的简易滤波实现
c语言·单片机·算法·51单片机
咖丨喱7 小时前
IP校验和算法解析与实现
网络·tcp/ip·算法