树可以看成是一个连通且 无环 的 无向 图。
给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。
示例 1:
输入: edges = [[1,2], [1,3], [2,3]]
输出: [2,3]
示例 2:
输入: edges = [[1,2], [2,3], [3,4], [1,4], [1,5]]
输出: [1,4]
提示:
n == edges.length3 <= n <= 1000edges[i].length == 21 <= ai < bi <= edges.lengthai != biedges中无重复元素- 给定的图是连通的
解题思路
本题要找出冗余的连接,即当前某个连接不连接,依然可以保证所有节点已经连通。
所以可以遍历输入数组,将两个节点合并到一个集合,如果遍历到某条边,当前两节点已经处于同一集合,则说明该边是一条可以删除的边。
那么在这个过程中我们需要获取元素所在集合,以及合并两个元素所在的集合。对于这种问题,可以使用并查集来解题。如果你对 并查集 还不了解,可以看我这一篇文章 数据结构-并查集,文中介绍了并查集的概念、应用场景以及手写实现的全过程。
代码实现
class UnionSet {
constructor(n){
// 初始化节点数量数组
this.size = Array(n).fill(1);
// 初始化集合列表,每一个节点为一个集合
this.list = Array(n)
for(let i = 0;i<n;i++){
this.list[i] = i;
}
}
// 递归获取元素所在集合根节点
find(x){
if(this.list[x]===x) return x;
// 获取到元素所在集合根节点
const root = this.find(this.list[x])
// 将当前节点挂载为根节点子节点,压缩路径
this.list[x] = root;
return root;
}
// 合并两个元素所在集合
merge(a,b){
// 获取两个元素所在集合的根节点
const rootA = this.find(a),
rootB = this.find(b);
// 如果两个根节点相同,则两元素处于同一集合,无需再合并
if(rootA===rootB) return;
// 如果 a 所在集合元素数量更多,将 b 所在集合合并到 a 所在集合
if(this.size[rootA]>this.size[rootB]){
this.list[rootB] = rootA;
this.size[rootA] += this.size[rootB]
}else{
// 反之将 a 所在集合合并到 b 所在集合
this.list[rootA] = rootB;
this.size[rootB] += this.size[rootA]
}
}
}
var findRedundantConnection = function(edges) {
// 获取连接数量
const len = edges.length,
// 创建并查集实例
unionset = new UnionSet(len+1);
// 遍历连接
for(let i = 0;i<len;i++){
const [a,b] = edges[i]
// 如果当前两个节点已经连接,则说明当前连接是冗余连接,返回当前连接
if(unionset.find(a)===unionset.find(b)) return [a,b]
// 连接当前连接两个节点
unionset.merge(a,b)
}
};
至此我们就完成了 leetcode-684-冗余连接
如有任何问题或建议,欢迎留言讨论!