leetcode684:冗余连接
问题描述
在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着N个节点 (节点值不重复1, 2, ..., N) 的树及一条附加的边构成。附加的边的两个顶点包含在1到N中间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组。每一个边的元素是一对[u, v] ,满足 u < v,表示连接顶点u 和v的无向图的边。
返回一条可以删去的边,使得结果图是一个有着N个节点的树。如果有多个答案,则返回二维数组中最后出现的边。答案边 [u, v] 应满足相同的格式 u < v。
解决方法:并查集
在一棵树中,边的数量比节点的数量少 1。如果一棵树有 N 个节点,则这棵树有 N-1条边。这道题中的图在树的基础上多了一条附加的边,因此边的数量也是 N。
树是一个连通且无环的无向图,在树中多了一条附加的边之后就会出现环,因此附加的边即为导致环出现的边。可以通过并查集寻找附加的边。
初始时,每个节点都属于不同的连通分量。遍历每一条边,判断这条边的两个顶点是否属于同一连通分量——即查看顶点的根节点是否相同。
-
不同,则两个顶点属于不同的连通分量。说明在遍历到当前边之前,这两个顶点之间不连通,因此当前的边不会导致环出现,合并这两个顶点的连通分量。
-
相同,则两个顶点属于同一连通分量,则说明在遍历到当前的边之前,这两个顶点之间已经连通,因此当前的边导致环出现,为附加的边,将当前的边作为答案返回。
代码
class Solution {
public int[] findRedundantConnection(int[][] edges) {
int vertex = edges.length;
//初始化并查集的父节点
int[] parent = new int[vertex+1];
for (int i=1; i<=vertex; i++){ //注意:因为顶点编号从1开始,所以此处索引起点设置为1。
parent[i] = i;
}
//定义一个list,保存所有会构成环的边(多余的边)
ArrayList<int[]> list = new ArrayList<>();
//遍历每条边
for (int[] edge : edges){
int node1 = edge[0];
int node2 = edge[1];
//找到边的两顶点的根节点
int rootX = find(parent, node1);
int rootY = find(parent, node2);
//根节点不同,则表明该边的两个顶点位于不同联通分支
if (rootX != rootY){
parent[find(parent, rootX)] = find(parent, rootY);
}
//根节点相同,则表明该边的两个顶点位于同一联通分支
else{
list.add(edge);
}
}
return list.get(list.size()-1); //取出最后出现的边
}
//注意:find函数需要将父节点数组parent传过来
public int find(int[] parent, int x){
if (x != parent[x]){
return find(parent, parent[x]);
}
else
return parent[x];
}
}
复杂度
时间复杂度:O(NlogN),其中 N 是图中的节点个数。需要遍历图中的 NN条边,对于每条边,需要查找其两个节点的根节点,如果两个节点的根节点不同则需要进行合并,需要进行 2 次查找和最多 1 次合并。一共需要进行 2N 次查找和最多 N 次合并,因此总时间复杂度是O(2NlogN)=O(NlogN)。这里的并查集使用了路径压缩,但是没有使用按秩合并,最坏情况下的时间复杂度是 O(NlogN),平均情况下的时间复杂度依然是 O(Nα(N)),其中 α 为阿克曼函数的反函数,α(N) 可以认为是一个很小的常数。
空间复杂度:O(N),其中 N 是图中的节点个数。使用数组 parent记录每个节点的父节点
作者:LeetCode-Solution
链接:https://leetcode-cn.com/problems/redundant-connection/solution/rong-yu-lian-jie-by-leetcode-solution-pks2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。