算法代码实现之Union-Find,Golang(Go语言)实现,quick-find、quick-union、加权quick-union(附带路径压缩优化)

QQ245671051 · · 1897 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

本算法主要解决动态连通性一类问题,这里尽量用精炼简洁的话来阐述。


数据结构描述:

  1. 有N个节点(索引0~N-1),可以查询节点数量
  2. 可以连接两个节点
  3. 可以查询两个节点是否连通
算法大致设计思路:
  1. 每个节点初始化为不同的整数标记
  2. 通过一个辅助函数查询某个节点的标记值
  3. 如果两节点标记相同,说明两节点是连通的
用一个包专门处理union-find算法(unionfind)
定义接口和基类(union_find.go):
package unionfind
 
//union-find的quick-find实现版
type QuickFind struct {
BaseUnionFind
}
 
func (qf *QuickFind) Connected(p, q int) bool {
return qf.id[p] == qf.id[q]
}
 
func (qf *QuickFind) Union(p, q int) {
i, j := qf.id[p], qf.id[q]
if i == j {
return
}
for k, v := range qf.id {
if v == j {
qf.id[k] = i
}
}
qf.count--
}
 
func NewQuickFind(n int) *QuickFind {
return &QuickFind{*NewBaseUnionFind(n)}
}

QuickFind:

  1. a和b进行union的时候,将b及与b连通节点的标记都置为和a的标记一样
  2. 标记相同的节点是连通的
quick_find.go:
package unionfind
 
//union-find的quick-find实现版
type QuickFind struct {
BaseUnionFind
}
 
func (qf *QuickFind) Connected(i, j int) bool {
return qf.id[i] == qf.id[j]
}
 
func (qf *QuickFind) Union(i, j int) {
c := qf.id[j]
for k, v := range qf.id {
if v == c {
qf.id[k] = qf.id[i]
}
}
qf.count--
}
 
func NewQuickFind(n int) *QuickFind {
return &QuickFind{*NewBaseUnionFind(n)}
}

QuickUnion:

  1. 连通的节点形成一棵树,根节点相同
quick_union.go:
package unionfind
 
//union-find的quick-union实现版
type QuickUnion struct {
BaseUnionFind
}
 
//查询根节点
func (qu *QuickUnion) findRoot(p int) int {
for p != qu.id[p] {
p = qu.id[p]
}
return p
}
 
func (qu *QuickUnion) Connected(p, q int) bool {
return qu.findRoot(p) == qu.findRoot(q)
}
 
func (qu *QuickUnion) Union(p, q int) {
rp := qu.findRoot(p)
rq := qu.findRoot(q)
if rp == rq {
return
}
qu.id[rq] = rp
qu.count--
}
 
func NewQuickUnion(n int) *QuickUnion {
return &QuickUnion{*NewBaseUnionFind(n)}
}

加权QuickUnion(附带路径压缩优化):

  1. union的时候小树挂在大树下
  2. 查询根节点的时候顺便将该节点的父节点直接指向根节点,压缩路径
weighted_quick_union.go:
package unionfind
 
 
//union-find的加权quick-union实现版,
//另外还作了路径压缩优化
type WeightedQuickUnion struct {
QuickUnion
sz []int
}
 
//查询根节点,顺便压缩路径
func (wqu *WeightedQuickUnion) findRoot(p int) int {
for p != wqu.id[p] {
wqu.id[p] = wqu.id[wqu.id[p]]
p = wqu.id[p]
}
return p
}
 
func (wqu *WeightedQuickUnion) Union(p, q int) {
rp := wqu.findRoot(p)
rq := wqu.findRoot(q)
if rp == rq {
return
}
if wqu.sz[rp] < wqu.sz[rq] {
wqu.id[rp] = rq
wqu.sz[rq] += wqu.sz[rp]
} else {
wqu.id[rp] = rq
wqu.sz[rp] += wqu.sz[rq]
}
wqu.count--
}
 
func NewWeightedQuickUnion(n int) *WeightedQuickUnion {
sz := make([]int, n)
for i := 0; i < n; i++ {
sz[i] = 1
}
return &WeightedQuickUnion{QuickUnion:*NewQuickUnion(n), sz: sz}
}


有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1897 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传