题目:
树可以看成是一个连通且 无环 的 无向 图。
给定往一棵 n 个节点 (节点值 1~n) 的树中添加一条边后的图。添加的边的两个顶点包含在 1 到 n 中间,且这条附加的边不属于树中已存在的边。图的信息记录于长度为 n 的二维数组 edges ,edges[i] = [ai, bi] 表示图中在 ai 和 bi 之间存在一条边。
请找出一条可以删去的边,删除后可使得剩余部分是一个有着 n 个节点的树。如果有多个答案,则返回数组 edges 中最后出现的边。
算法:
方法一:并查集
思路:遍历所有edges,将所有顶点加入并查集,如果加入后联通了,则说明形成了环,这条边可以去掉 。
因为只增加了一条边,所以第一次检测到联通的边就是(最后一条边)。比如```
edges = [[1,2], [1,3], [2,3]]
结果是[1,2][1,3]都可以,但是要最后一条边,其实是第一条导致成环的边
var unionFind []int
func findRedundantConnection(edges [][]int) []int {
n := len(edges)
unionFind = make([]int, n + 1)
for i := range unionFind {
unionFind[i] = i
}
ans := make([]int, 0)
for i := range edges {
if isConnect(edges[i][0], edges[i][1]) {
ans = edges[i]
break
}
}
return ans
}
func find(x int) int {
xf := unionFind[x]
if x != xf {
unionFind[x] = find(xf)
}
return unionFind[x]
}
func isConnect(x, y int) bool {
xf := find(x)
yf := find(y)
if xf == yf {
return true
}
unionFind[xf] = yf
return false
}
方法二:拓补排序
拓补排序,统计所有节点的度,将度为1的节点从邻接表中删除
最后从edges逆序遍历,从邻接表找到一条度为2的边就是结果
func findRedundantConnection(edges [][]int) []int {
n := len(edges)
neighbors := make([][]int, n + 1)
degree := make([]int, n + 1)
// 统计入度
for i := range edges {
a, b := edges[i][0], edges[i][1]
degree[a] ++
degree[b] ++
neighbors[a] = append(neighbors[a], b)
neighbors[b] = append(neighbors[b], a)
}
// 将度为1的节点出队列
vec := make([]int, 0)
for i := range degree {
if degree[i] == 1 {
vec = append(vec, i)
}
}
// 将度为1的节点出队列,将它和邻居的度-1,vec
for len(vec) != 0 {
node := vec[0]
vec = vec[1:]
for _, neighbor := range neighbors[node] {
degree[node] --
degree[neighbor] --
if degree[neighbor] == 1 {
vec = append(vec, neighbor)
}
}
}
// 最后degree中只有度为0,2的节点
// 从edges逆序遍历,从邻接表找到一条度为2的边就是结果
var ans []int
for i := n - 1; i >= 0; i -- {
if degree[edges[i][0]] == 2 && degree[edges[i][1]] == 2 {
ans = edges[i]
break
}
}
return ans
}