1.并查集的概念
并查集常用来解决连通性问题。(判断两个元素是否在同一个集合中)
- 两个主要功能
- 将两个元素添加到一个集合中
- 判断两个元素在不在同一个集合
2.并查集的模板
type Disjoint struct {
NodeNum int
father []int
}
// 并查集初始化
func (d *Disjoint) init() {
d.father = make([]int, d.NodeNum)
for i := 0; i < d.NodeNum; i++ {
d.father[i] = i
}
}
// 并查集里寻根的过程,
func (d *Disjoint) find(u int) int {
if u == d.father[u] {
return u
} else {
//路径压缩
d.father[u] = d.find(d.father[u])
return d.father[u]
}
}
// 判断u和v是否找到同一个根
func (d *Disjoint) isSameFather(u,v int) bool {
u = d.find(u)
v = d.find(v)
return u == v
}
// 将v->u这条件加入并查集
func (d *Disjoint) join(u,v int) {
u = d.find(u)
v = d.find(v)
if u == v{
return
}
d.father[v]=u
}
3.习题
3.1 寻找存在的路径
package main
import "fmt"
var N, M int
func main() {
fmt.Scan(&N, &M)
disjoint := Disjoint{NodeNum: N}
disjoint.init()
var tempA, tempB int
for i := 0; i < M; i++ {
fmt.Scan(&tempA, &tempB)
disjoint.join(tempA, tempB)
}
fmt.Scan(&tempA, &tempB)
if disjoint.isSameFather(tempA, tempB) {
fmt.Println(1)
} else {
fmt.Println(0)
}
}
type Disjoint struct {
NodeNum int
father []int
}
// 并查集初始化
func (d *Disjoint) init() {
d.father = make([]int, d.NodeNum+1)
for i := 0; i < d.NodeNum+1; i++ {
d.father[i] = i
}
}
// 并查集里寻根的过程,
func (d *Disjoint) find(u int) int {
if u == d.father[u] {
return u
} else {
//路径压缩
d.father[u] = d.find(d.father[u])
return d.father[u]
}
}
// 判断u和v是否找到同一个根
func (d *Disjoint) isSameFather(u, v int) bool {
u = d.find(u)
v = d.find(v)
return u == v
}
// 将v->u这条件加入并查集
func (d *Disjoint) join(u, v int) {
u = d.find(u)
v = d.find(v)
if u == v {
return
}
d.father[v] = u
}
3.2 冗余连接
package main
import "fmt"
var N int
func main() {
fmt.Scan(&N)
disjoint := Disjoint{NodeNum: N}
disjoint.init()
var tempA, tempB int
for i := 0; i < N; i++ {
fmt.Scan(&tempA, &tempB)
if disjoint.isSameFather(tempA, tempB) {
fmt.Printf("%d %d\n", tempA, tempB)
return
}
disjoint.join(tempA, tempB)
}
if disjoint.isSameFather(tempA, tempB) {
fmt.Println(1)
} else {
fmt.Println(0)
}
}
type Disjoint struct {
NodeNum int
father []int
}
// 并查集初始化
func (d *Disjoint) init() {
d.father = make([]int, d.NodeNum+1)
for i := 0; i < d.NodeNum+1; i++ {
d.father[i] = i
}
}
// 并查集里寻根的过程,
func (d *Disjoint) find(u int) int {
if u == d.father[u] {
return u
} else {
//路径压缩
d.father[u] = d.find(d.father[u])
return d.father[u]
}
}
// 判断u和v是否找到同一个根
func (d *Disjoint) isSameFather(u, v int) bool {
u = d.find(u)
v = d.find(v)
return u == v
}
// 将v->u这条件加入并查集
func (d *Disjoint) join(u, v int) {
u = d.find(u)
v = d.find(v)
if u == v {
return
}
d.father[v] = u
}
3.3 冗余链接II
package main
import "fmt"
var N int
func main() {
fmt.Scan(&N)
inDegree := make([]int, N+1)
edges := make([][2]int, 0)
var tempA, tempB int
for i := 0; i < N; i++ {
fmt.Scan(&tempA, &tempB)
inDegree[tempB] += 1
edges = append(edges, [2]int{tempA, tempB})
}
// 情况1:记录入度为2的边(如果有的话就两条边)
vec := make([]int, 0)
for i := N - 1; i >= 0; i-- {
if inDegree[edges[i][1]] == 2 {
vec = append(vec, i)
}
}
if len(vec) != 0 {
// 放在vec里的边已经按照倒叙放的,所以这里就优先删vec[0]这条边
if isTreeAfterRemoveEdge(edges, vec[0]) {
fmt.Printf("%d %d\n", edges[vec[0]][0], edges[vec[0]][1])
return
} else {
fmt.Printf("%d %d\n", edges[vec[1]][0], edges[vec[1]][1])
return
}
}
// 情况2:明确没有入度为2的情况,那么一定有有向环,找到构成环的边返回就可以了
getRemoveEdge(edges)
}
func isTreeAfterRemoveEdge(edges [][2]int, deleteEdge int) bool {
disjoint := Disjoint{NodeNum: N}
disjoint.init()
for i := 0; i < N; i++ {
if i == deleteEdge {
continue
}
if disjoint.isSameFather(edges[i][0], edges[i][1]) {
// 构成有向环了,一定不是树
return false
}
disjoint.join(edges[i][0], edges[i][1])
}
return true
}
func getRemoveEdge(edges [][2]int) {
disjoint := Disjoint{NodeNum: N}
disjoint.init()
for i := 0; i < N; i++ {
if disjoint.isSameFather(edges[i][0], edges[i][1]) {
// 构成有向环了,一定不是树
fmt.Printf("%d %d\n", edges[i][0], edges[i][1])
return
}
disjoint.join(edges[i][0], edges[i][1])
}
}
type Disjoint struct {
NodeNum int
father []int
}
// 并查集初始化
func (d *Disjoint) init() {
d.father = make([]int, d.NodeNum+1)
for i := 0; i < d.NodeNum+1; i++ {
d.father[i] = i
}
}
// 并查集里寻根的过程,
func (d *Disjoint) find(u int) int {
if u == d.father[u] {
return u
} else {
//路径压缩
d.father[u] = d.find(d.father[u])
return d.father[u]
}
}
// 判断u和v是否找到同一个根
func (d *Disjoint) isSameFather(u, v int) bool {
u = d.find(u)
v = d.find(v)
return u == v
}
// 将start->to这条件加入并查集
func (d *Disjoint) join(start, to int) {
to = d.find(to)
start = d.find(start)
if to == start {
return
}
d.father[start] = to
}