并查集的应用
题目1:684. 冗余连接
属于常规并查集的应用, 依次加入每条边
如果发现某条边的两个节点已经在一个集合当中,那么就是冗余的边了。
class Solution {
func findRedundantConnection(_ edges: [[Int]]) -> [Int] {
var parent = Array(repeating: 0, count: edges.count + 1)
for i in 0...edges.count {
parent[i] = i
}
func findRoot(_ u: Int) -> Int {
if u == parent[u] { return u }
parent[u] = findRoot(parent[u])
return parent[u]
}
func join(_ v: Int, _ u: Int) {
let v = findRoot(v)
let u = findRoot(u)
if u == v { return }
parent[u] = v
}
func isSame(_ v: Int, _ u: Int) -> Bool {
findRoot(u) == findRoot(v)
}
for edge in edges {
if isSame(edge[0], edge[1]) {
return edge
} else {
join(edge[0], edge[1])
}
}
return []
}
}
题目2:685. 冗余连接 II
有向树 相较于 无向树, 就是构成环的边不能无脑删除。
需要配合入度的检查。
而入度检查的删除也并不是随便删一个就可以, 需要判断删哪条可行,依次遍历并且检查就好。
比如这个case[[1, 3], [2, 3], [1, 2]] , 无论是什么顺序都不能删除 [1, 2],删除其他两条中任意一条都可以。
但是如果不考虑入度,直接按照冗余连接1的做法,答案对不对就看输入的顺序了, 因为他始终会删除最后一个元素。
class Solution {
func findRedundantDirectedConnection(_ edges: [[Int]]) -> [Int] {
var parent = [Int]()
func initParent() {
parent = Array(repeating: 0, count: edges.count + 1)
for i in 1...edges.count {
parent[i] = i
}
}
func findRoot(_ u: Int) -> Int {
if u == parent[u] { return u }
parent[u] = findRoot(parent[u])
return parent[u]
}
func isSame(_ u: Int, _ v: Int) -> Bool {
findRoot(u) == findRoot(v)
}
func join(_ u: Int, _ v: Int) {
let u = findRoot(u)
let v = findRoot(v)
if u == v { return }
parent[v] = u
}
var digree = Array(repeating: 0, count: edges.count + 1)
// 统计入度
edges.forEach { digree[$0[1]] += 1 }
// 尝试删除导致入度为2的边
func delete2DigreeEdge(edgeIndex: Int) -> Bool {
initParent()
for (i, edge) in edges.enumerated() {
if i == edgeIndex { continue }
if isSame(edge[0], edge[1]) { return false }
join(edge[0], edge[1])
}
return true
}
for i in (0..<edges.count).reversed() {
if digree[edges[i][1]] == 2, delete2DigreeEdge(edgeIndex: i) {
return edges[i]
}
}
// 没有入度为2的点,删除构成环的边。
initParent()
for (i, edge) in edges.enumerated() {
if isSame(edge[0], edge[1]) { return edge }
join(edge[0], edge[1])
}
return []
}
}