package UnionFind
import (
"fmt"
"testing"
)
/*
并查集
1. 有若干个样本 a、b、c、d... 类型假设是V
2. 在并查集中一开始认为每个样本都在单独的集合里
3. 用户可以在任何时候调用如下两个方法
bool Find(V x, V y):查询样本x 和样本y 是否属于一个集合
void Union(V x, V y):把x,y 各自所在集合的所有样本合并成一个集合,两个集团后的东西全部放在一起
4. Find, 和 Union 方法的代价越低越好,最好O(1)
*/
/*
V: a,b,c,d,e
a,b,c,d,e 加一个指针指向自己 [a']
用一个map 维护 a 对应的 a'
Find a -> a' a' 不停的往上查找 找到不能再往上的时候返回,找到的叫代表点, 代表点不一样,不在一个集合里
Union a -> a' e -> e' 用比较小的集合 挂在较大的集合代表点上。 大小一样随意挂 代表点相同 不 union
两个优化:链扁平化
小挂大 减少遍历链的高度
复杂度
假设N个节点,如果findFather 调用的非常频繁,O(N) or O(>N) 越频繁,代价越低,单次
1964问世 - 1989年才证明出来
连通性问题 全这么做
*/
type Node struct {
value interface{}
}
type UnionSet struct{
nodes map[interface{}]*Node // V --> 节点对应表
parents map[*Node]*Node // 父节点
sizeMap map[*Node]int // 只有一个点是某一个集合的代表点,才会有记录其大小
}
func NewUnionSet(values []interface{}) *UnionSet { // 按引用传递 可以区分重复值
us := &UnionSet{
nodes: map[interface{}]*Node{},
parents: map[*Node]*Node{},
sizeMap: map[*Node]int{},
}
for k := range values {
node := &Node{value: values[k]}
us.nodes[values[k]]=node
us.parents[node] = node
us.sizeMap[node] = 1
}
return us
}
func (u *UnionSet)findFather(cur *Node) *Node {
path := NewStack()
for cur != u.parents[cur] {
path.Push(cur)
cur = u.parents[cur]
}
for !path.IsEmpty() { //优化:扁平化
u.parents[path.Pop().(*Node)] = cur
}
return cur
}
func (u *UnionSet)Find(a, b interface{}) bool {
_,ok1 := u.nodes[a]
_,ok2 := u.nodes[b]
if !ok1 || !ok2 {
return false
}
return u.findFather(u.nodes[a]) == u.findFather(u.nodes[b])
}
func (u *UnionSet)Union(a, b interface{}) {
_,ok1 := u.nodes[a]
_,ok2 := u.nodes[b]
if !ok1 || !ok2 {
fmt.Println(a,b)
return
}
aHead := u.findFather(u.nodes[a])
bHead := u.findFather(u.nodes[b])
if aHead != bHead {
aSetSize := u.sizeMap[aHead]
bSetSize := u.sizeMap[bHead]
if aSetSize >= bSetSize {
u.parents[bHead] = aHead
u.sizeMap[aHead] = aSetSize + bSetSize
delete(u.sizeMap,bHead)
}else {
u.parents[aHead] = bHead
u.sizeMap[bHead] = aSetSize + bSetSize
delete(u.sizeMap,aHead)
}
}
}
func (u *UnionSet)getsizeMapLength() int {
return len(u.sizeMap)
}
/*
学生实例 baiduID githubID bilibiliID
三个属性,任何两个id 一样,就算同一个人
*/
type user struct {
A,B,C string
}
/*
如果两个user,a字段一样,或者b字段一样,或者c字段一样,就认为是一个人
请合并users,返回合并之后的用户数量
(1,10,13)
(2,10,37)
(400,500,37)
*/
func mergeUsers(users []*user) int {
tmp := make([]interface{},len(users))
for k := range users{
tmp[k] = users[k]
}
unionFind := NewUnionSet(tmp)
mapA := map[string]*user{}
mapB := map[string]*user{}
mapC := map[string]*user{}
for k := range users {
if _, ok := mapA[users[k].A] ;ok {
unionFind.Union(users[k],mapA[users[k].A])
}else {
mapA[users[k].A] = users[k]
}
if _, ok := mapB[users[k].B] ;ok {
unionFind.Union(users[k],mapB[users[k].B])
}else{
mapB[users[k].B] = users[k]
}
if _, ok := mapC[users[k].C] ;ok {
unionFind.Union(users[k],mapC[users[k].C])
}else{
mapC[users[k].C] = users[k]
}
}
//向并查集询问,合并之后还剩多少个集合
return unionFind.getsizeMapLength()
}
func TestMergeUsers(t *testing.T) {
users := []*user{
{"1","2","3"},
{"4","5","3"},
{"2","3","4"},
{"18","20","5"},
{"19","21","6"},
}
fmt.Println(mergeUsers(users))
}
func TestUnionFind(t *testing.T) {
str := []interface{}{"1","2","3"}
unionFind := NewUnionSet(str)
unionFind.Union("1","2")
unionFind.Union("2","3")
fmt.Println(unionFind.Find("1","3"))
}
/*
并查集
1. 每个节点都有一条往上指的指针
2. 节点a往上找到的头节点,叫做a所在集合的代表点
3. 查询x 和y是否属于同一个集合,就是看看找到的代表点是不是一个
4. 把x和y各自所在集合的所有点合并成一个集合,只需要小集合的代表点挂在大集合的代表点的下方即可
并查集的优化
1.节点往上找代表点的过程,把沿途的链变成扁平的
2.小集合挂在大集合的下面
3.如果方法调用很频繁,那么单次调用的代价为O(1),两个方法都如此
*/
type Stack struct {
Elem []interface{}
}
func NewStack() *Stack {
return &Stack{Elem: []interface{}{}}
}
func (s *Stack)Pop() interface{} {
res := s.Elem[len(s.Elem)-1]
s.Elem = s.Elem[:len(s.Elem)-1]
return res
}
func (s *Stack)Push(value interface{}) {
s.Elem = append(s.Elem,value)
}
func (s *Stack)IsEmpty() bool {
return len(s.Elem) == 0
}
func (s *Stack)Peek() interface{} {
return s.Elem[len(s.Elem)-1]
}
type Union interface {
Union(p, q interface{}) // Join adds a connection between two elements.
Find(p interface{}) interface{} // Find finds the parent of a given element.
IsConnected(p, q interface{}) bool // Connected checks if two elements are connected.
GetMaxSetsLenAndParent() (int, interface{})
GetRankSet()map[interface{}]int
GetRankLen() int
GetSet() []interface{}
GetSetLen() int
GetParentSet() map[interface{}]interface{}
GetParentsLen() int
}
type union struct {
parent map[interface{}]interface{}
rank map[interface{}]int
sets map[interface{}]struct{}
maxIs interface{}
}
func NewUnionFind() Union {
return &union{
parent: make(map[interface{}]interface{}),
rank: make(map[interface{}]int),
sets: map[interface{}]struct{}{},
maxIs: new(interface{}),
}
}
func (this *union) Find(p interface{}) interface{} {
root := p
for q,ok := this.parent[root]; ok; { //找到元素的根
root = q
q,ok = this.parent[root]
}
for p != root { //扁平化处理,让所有元素都指向根,压缩元素之间定位根的复杂度
q := this.parent[p]
this.parent[p], p = root, q
}
return root
}
func (this *union) Union(p, q interface{}) {
if _,ok:=this.sets[p];!ok {
this.sets[p]=struct {}{}
}
if _,ok:=this.sets[q];!ok{
this.sets[q]=struct{}{}
}
pr,qr := this.Find(p),this.Find(q)
if pr == qr {
return
}
max:=map[bool]interface{}{this.rank[pr] >= this.rank[qr]:pr, this.rank[pr] < this.rank[qr] :qr}[true]
min:=map[bool]interface{}{this.rank[pr] >= this.rank[qr]:pr, this.rank[pr] < this.rank[qr] :qr}[false]
this.parent[min]=max
this.rank[max]=this.rank[max]+this.rank[min]+1
delete(this.rank,min)
if this.maxIs==nil || this.rank[this.maxIs]<this.rank[max]{
this.maxIs=max
}
}
func (this *union) IsConnected(p, q interface{}) bool {
return this.Find(p) == this.Find(q)
}
func (this *union) GetMaxSetsLenAndParent() (int, interface{}) {
return this.rank[this.maxIs],this.maxIs
}
func (this *union)GetRankSet()map[interface{}]int {
return this.rank
}
func (this *union)GetRankLen() int {
return len(this.rank)
}
func (this *union)GetSet() []interface{} {
var v []interface{}
for k,_:=range this.sets{
v = append(v, k)
}
return v
}
func (this *union)GetSetLen() int {
return len(this.sets)
}
func (this *union)GetParentSet() map[interface{}]interface{} {
return this.parent
}
func (this *union)GetParentsLen() int {
return len(this.parent)
}
func TestUnionFind1(t *testing.T) {
unionFind := NewUnionFind()
unionFind.Union(1, 3)
unionFind.Union(3, 4)
unionFind.Union(2, 5)
unionFind.Union(6, 7)
unionFind.Union(8, 9)
unionFind.Union(7, 8)
unionFind.Union(6, 4)
unionFind.Union(2, 1)
unionFind.Union(21,131)
fmt.Println(unionFind.Find(9))
fmt.Println(unionFind.Find(6))
fmt.Println(unionFind.Find(21))
fmt.Println(unionFind.IsConnected(1,2))
fmt.Println(unionFind.GetParentSet())
fmt.Println(unionFind.GetParentsLen())
fmt.Println(unionFind.GetRankSet())
fmt.Println(unionFind.GetRankLen())
fmt.Println(unionFind.GetSet())
fmt.Println(unionFind.GetSetLen())
fmt.Println(unionFind.GetMaxSetsLenAndParent())
}
并查集汇总
于 2021-10-27 19:01:16 首次发布