【代码随想录——图论——并查集】

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
}
  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值