685.冗余连接 II

130 阅读3分钟

题目:
在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。

输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。

结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。

返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。

算法:
方法一:并查集 + 分类讨论
一棵树增加一条有向边,可能有两种情况:
1.有顶点入度为2 ,可能有,也可能没有有向环 tree2.png

2.没有顶点入度为2,则构成有向环 tree.png

不可能既构成环,又有顶点入度为2,搞清楚了这一点,先统计所有顶点的入度然后分类讨论即可。

如果是情况1,顶点入度为2,则说明有两条有向边指向该顶点,按照这两条边再edges的出现顺序逆序check,有一条边移除后构成树(即不是环,有环就不是树了),则返回该条边。

如果是情况2,则和《684.冗余连接I》相同,通过并查集返回第一条导致构成环的边即可

要能想到这两种情况可不容易!为什么会想到用入度是否为2来分类呢!因为题目中说了:”该树除了根节点之外的每一个节点都有且只有一个父节点“。据此对树增加一条边多画画图,看看会变成怎么样,有哪些情况。

参考:leetcode.cn/problems/re…

var unionFind []int
func findRedundantDirectedConnection(edges [][]int) []int {
	n := len(edges)
	inDegree := make([]int, n + 1)
	unionFind = make([]int, n + 1)
	vec := make([]int, 0)
	// 统计入度
	for i := range edges {
		inDegree[edges[i][1]] ++
	}
	// 判断是否存在入度为2的顶点,存在则有两边有向边指向该顶点,逆序将这两条边加入vec数组 
	for i := len(edges) - 1; i >= 0; i -- {
		if inDegree[edges[i][1]] == 2 {
			vec = append(vec, i)
		}
	}
	// fmt.Println(vec)
	// 看移除哪一条边之后,还是树。如果vec的第一条边满足条件则直接返回第一条边
	if len(vec) > 0 {
		if isTreeAfterRemoved(vec[0], edges) {
			return edges[vec[0]]
		} else {
			return edges[vec[1]]
		}

	}

	// 没有入度为2的顶点,找到移除后能破坏环的最后一条边
	return getRemovedEdge(edges)
}

// 移除edges[index]后不能构成环,则可以构成数
func isTreeAfterRemoved(removeIndex int, edges [][]int) bool {
	// 初始化并查集
	for i := range unionFind {
		unionFind[i] = i
	}
	// 遍历edges,跳过index,构成环了则返回false
	for i := range edges {
		if i == removeIndex {
			continue
		}
		if connect(edges[i][0], edges[i][1]) {
			return false
		}
		union(edges[i][1], edges[i][0])
	}

	return true
}

// 找到移除后不再构成环的第一条边
func getRemovedEdge(edges [][]int) []int {
	// 初始化并查集
	for i := range unionFind {
		unionFind[i] = i
	}
	ans := make([]int, 0)
	// 遍历edges,第一次构成环的边就是它可以移除
	for i := range edges { 
		if connect(edges[i][0], edges[i][1]) {
			ans = edges[i]
			break
		}
		union(edges[i][1], edges[i][0])
	}
	return ans
}

func connect(x, y int) bool {
	xf := find(x)
	yf := find(y)
	return xf == yf
}

func union(x, y int) {
	xf := find(x)
	yf := find(y)
	if xf != yf {
		unionFind[x] = yf
	}
}

func find(x int) int {
	xf := unionFind[x]
	if xf != x {
		unionFind[x] = find(xf)
	}
	return unionFind[x]
}