684. 冗余连接

139 阅读2分钟

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

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

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

算法:
方法一:并查集
思路:遍历所有edges,将所有顶点加入并查集,如果加入后联通了,则说明形成了环,这条边可以去掉 。
因为只增加了一条边,所以第一次检测到联通的边就是(最后一条边)。比如``` edges = [[1,2], [1,3], [2,3]]

image.png
结果是[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
}