并查集汇总

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())
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

metabit

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值