力扣每日一题 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;
};
相关推荐
情怀姑娘23 分钟前
面试题---------------场景+算法
java·算法·mybatis
chbmvdd29 分钟前
week5题解
数据结构·c++·算法
用户120391129472630 分钟前
面试官最爱问的字符串反转:7种JavaScript实现方法详解
算法·面试
vir0238 分钟前
小齐的技能团队(dp)
数据结构·c++·算法·图论
Star在努力1 小时前
C语言复习八(2025.11.18)
c语言·算法·排序算法
南山安1 小时前
从反转字符串看透面试官的“内心戏”:你的算法思维到底怎么样?
javascript·算法·面试
雪不下1 小时前
计算机中的数学:概率(2)
算法
zs宝来了1 小时前
HOT100-二分查找类型题
算法
_w_z_j_1 小时前
数组中的最长连续子序列
数据结构·算法
地平线开发者2 小时前
征程 6E/M 计算平台部署指南
算法·自动驾驶