题目:
在本问题中,有根树指满足以下条件的 有向 图。该树只有一个根节点,所有其他节点都是该根节点的后继。该树除了根节点之外的每一个节点都有且只有一个父节点,而根节点没有父节点。
输入一个有向图,该图由一个有着 n 个节点(节点值不重复,从 1 到 n)的树及一条附加的有向边构成。附加的边包含在 1 到 n 中的两个不同顶点间,这条附加的边不属于树中已存在的边。
结果图是一个以边组成的二维数组 edges 。 每个元素是一对 [ui, vi],用以表示 有向 图中连接顶点 ui 和顶点 vi 的边,其中 ui 是 vi 的一个父节点。
返回一条能删除的边,使得剩下的图是有 n 个节点的有根树。若有多个答案,返回最后出现在给定二维数组的答案。
算法:
方法一:并查集 + 分类讨论
一棵树增加一条有向边,可能有两种情况:
1.有顶点入度为2 ,可能有,也可能没有有向环
2.没有顶点入度为2,则构成有向环
不可能既构成环,又有顶点入度为2,搞清楚了这一点,先统计所有顶点的入度然后分类讨论即可。
如果是情况1,顶点入度为2,则说明有两条有向边指向该顶点,按照这两条边再edges的出现顺序逆序check,有一条边移除后构成树(即不是环,有环就不是树了),则返回该条边。
如果是情况2,则和《684.冗余连接I》相同,通过并查集返回第一条导致构成环的边即可
要能想到这两种情况可不容易!为什么会想到用入度是否为2来分类呢!因为题目中说了:”该树除了根节点之外的每一个节点都有且只有一个父节点“。据此对树增加一条边多画画图,看看会变成怎么样,有哪些情况。
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]
}