LeetCode 684 冗余连接 (中等)

在本问题中, 树指的是一个连通且无环的无向图。

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

结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u v无向图的边。

返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v

示例1:

输入: [[1,2], [1,3], [2,3]]
输出: [2,3]
解释: 给定的无向图为:
  1
 / \
2 - 3

示例2:

输入: [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
解释: 给定的无向图为:
5 - 1 - 2
    |   |
    4 - 3

注意:

  • 输入的二维数组大小在 3 到 1000。
  • 二维数组中的整数在1到N之间,其中N是输入数组的大小。

 思路 + 代码

  该图由一棵树和一条附加边组成,该附加边一定是使原树形成了环才破坏了树结构,现如今要找到这样一条边。

  经分析,其实破环一个环中间的任意一条边都可以再恢复成一棵树,但是题目要求返回返回二维数组中最后出现的边。

  并查集可以用来判断图中是否有环结构。

并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。

并查集是一种树型的数据结构,用于处理一些不相交集合(DisjointDisjoint SetsSets)的合并及查询问题。常常在使用中以森林来表示。

概念

  • 合并(UnionUnion):把两个不相交的集合合并为一个集合
  • 查询(FindFind):查询两个元素是否在同一个集合中

并查集标准代码

class UnionFindSet {
        int[] parents;

        UnionFindSet(int n) {
            // 记录每个元素的parent即根节点,先将它们的根节点设为自己
            parents = new int[n + 1];
            for (int i = 1; i <= n; i++)
                parents[i] = i;
        }
        
        // 合并树
        public void union(int x, int y){

            int xParent = findParent(x);
            int yParent = findParent(y);
            
            // 把x和y分属的两棵树连接起来,
            //这里可以优化,可以把深度小的树挂在深度大的树下面,减少findparent中迭代的次数
            parents[xParent] = yParent;

        }
        
        // 找某一个元素的根节点
        public int findParent(int son){
            while (son != parents[son]){
                son = parents[son];
            }
            return son;
        }
    }

 

  本题代码实现

  运用到本题中,其实就是判断一条边的两个节点是否有共同的根节点,如果有,说明他们已经属于同一棵树,那么就会形成环。

class Solution {
    int[] res = new int[2];

    public int[] findRedundantConnection(int[][] edges) {
        
        Union union = new Union(edges.length);

        for (int[] edge: edges)
            union.add(edge);

        return res;

    }

    // 并查集标准代码
    class Union {
        int[] parents;

        Union (int size) {
            parents = new int[size+1];
            for (int i = 1; i <= size; i++)
                parents[i] = i;
        }

        public void add(int[] edge){
            int x = edge[0];
            int y = edge[1];
            int xParent = findParent(x);
            int yParent = findParent(y);

            if (xParent != yParent)
                parents[xParent] = yParent;
            else {
                // 该部分是为了找到附加边 不是并查集原代码
                res[0] = x;
                res[1] = y;
            }
        }

        public int findParent(int son){
            while (son != parents[son]){
                son = parents[son];
            }
            return son;
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值