力扣每日一题 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;
};
相关推荐
爱编程— 的小李5 分钟前
有序序列合并(c语言)
c语言·算法
云卓SKYDROID6 分钟前
无人机反步滑膜控制算法!
算法·无人机·知识科普·云卓科技·反步滑膜控制算法
云卓科技6 分钟前
无人机之自动控制原理篇
科技·算法·目标检测·机器人·无人机
云卓科技8 分钟前
无人机之集群控制方法篇
科技·算法·无人机·交互·制造
混迹网络的权某9 分钟前
每天一道C语言精选编程题之求数字的每⼀位之和
c语言·开发语言·考研·算法·改行学it·1024程序员节
Curry_Math13 分钟前
LeetCode 热题 100之链表3
算法·leetcode·链表
hn小菜鸡3 小时前
LeetCode 2058.找出临界点之间的最小和最大距离
算法·leetcode·职场和发展
liuyang-neu3 小时前
力扣 简单 70.爬楼梯
java·算法·leetcode
IronmanJay3 小时前
【LeetCode每日一题】——862.和至少为 K 的最短子数组
数据结构·算法·leetcode·前缀和·双端队列·1024程序员节·和至少为 k 的最短子数组
OT.Ter3 小时前
【力扣打卡系列】二分查找(搜索旋转排序数组)
算法·leetcode·职场和发展·go·二分查找