在本问题中, 树指的是一个连通且无环的无向图。
输入一个图,该图由一个有着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;
}
}
}