684. 冗余连接

前言

「力扣题解」系列致力于分享有价值的题目、探讨更优秀的解法。

这是本系列的第 7 篇题解,更多题解关注 Somnia1337@力扣。

题目描述

难度:🟡中等

标签:#深度优先搜索广度优先搜索并查集

树可以看成是一个连通且 无环 的 无向 图。

给定往一棵 n 个结点(结点值 1~n)的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edgesedges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。

请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个结点的树。如果有多个答案,则返回数组 edges 中最后出现的那个。

输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]

概念复习

  • 简单图:无环、无平行边的图。
  • 支:图的一个极大连通子图。
  • 连通图:支的数量为 1 的图。

解题思路

「树」是一个无环无向的「连通图」,同时是一个「简单图」,因此在树中添加一条边之后一定会出现环。

遍历输入数组 edges 建图,此过程中,如果出现一条边,它使得其两个端点之间出现第 2 条道路,即出现了环,那么这条边就是要找的“冗余连接”。

通常用并查集维护结点之间的连通性,并查集在「数据结构与算法」学习过,简单概括就是:

  • 查找:找结点所在的「支」
  • 归并:将 2 个「支」合为 1 个

每个支用一个结点唯一代表,支可以看作等价类,代表结点就是代表元。

并查集初始化时,每个结点分别属于以自身代表的支,遍历 edges

  • 如果两个结点属于不同支,则在添加这条边之前,两个结点之间不连通,因此添加这条边不会产生环。添加它,然后合并两个支。
  • 如果两个结点属于相同支,则在添加这条边之前,两个结点之间已连通,因此添加这条边会产生环,返回这条边。

复杂度

  • 时间复杂度:,其中 为边数, 为反阿克曼函数,遍历时对 个结点进行路径压缩与按秩合并的复杂度为
  • 空间复杂度:

代码

Java
class Solution {
    public int[] findRedundantConnection(int[][] edges) {
        int[] root = new int[edges.length + 1]; // 并查集
        for (int i = 0; i <= edges.length; i++) root[i] = i;
        for (int[] edge : edges) {
            int n1 = edge[0], n2 = edge[1];
            // 如果所属支不同, 则合为 1 个
            if (find(root, n1) != find(root, n2)) union(root, n1, n2);
            // 否则, 这条边将导致环, 返回
            else return new int[]{n1, n2};
        }
        return null;
    }
	
    private int find(int[] root, int i) {
        while (root[i] != i) {
            root[i] = root[root[i]];
            i = root[i];
        }
        return i;
    }
	
    private void union(int[] root, int x, int y) {
        root[find(root, x)] = find(root, y);
    }
}
Python
class Solution:
    def findRedundantConnection(self, edges: List[List[int]]) -> List[int]:
        def find(i: int) -> int:
            if root[i] != i:
                root[i] = find(root[i])
            return root[i]
		
        def union(n1, n2):
            root[find(n1)] = find(n2)
        
        n = len(edges)
        root = list(range(n + 1)) # 并查集
        for n1, n2 in edges:
	        # 如果所属支不同, 则合为 1 个
            if find(n1) != find(n2):
                union(n1, n2)
            # 否则, 这条边将导致环, 返回
            else:
                return [n1, n2]
        return []

相关题目

685. 冗余连接 II

深度优先搜索广度优先搜索并查集

最后

更多系列「外源推文」&「生活分享」关注公众号: