力扣每日一题 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;
};
相关推荐
hweiyu0011 分钟前
数据结构和算法分类
数据结构·算法·分类
M K Q13 分钟前
2025.9 GESP三级 日历制作
算法
x***J34829 分钟前
算法竞赛训练方法
算法
前端小L32 分钟前
图论专题(十六):“依赖”的死结——用拓扑排序攻克「课程表」
数据结构·算法·深度优先·图论·宽度优先
前端小L32 分钟前
图论专题(十三):“边界”的救赎——逆向思维解救「被围绕的区域」
数据结构·算法·深度优先·图论
风筝在晴天搁浅37 分钟前
代码随想录 738.单调递增的数字
数据结构·算法
Miraitowa_cheems1 小时前
LeetCode算法日记 - Day 108: 01背包
数据结构·算法·leetcode·深度优先·动态规划
九年义务漏网鲨鱼1 小时前
【多模态大模型面经】现代大模型架构(一): 组注意力机制(GQA)和 RMSNorm
人工智能·深度学习·算法·架构·大模型·强化学习
闲人编程2 小时前
CPython与PyPy性能对比:不同解释器的优劣分析
python·算法·编译器·jit·cpython·codecapsule
杜子不疼.2 小时前
【C++】深入解析AVL树:平衡搜索树的核心概念与实现
android·c++·算法