• 算法第四版 Algorithms Part 1动态联通性


    联通性检测用途

    • 照片中的像素
    • 网络中的计算机
    • 社交网络中的朋友
    • 计算机芯片中的电路元器件
    • 数学集合中的元素
    • Fortan程序中的变量
    • 复合体系中的金属位
      在这里插入图片描述

    假定已连接等价于

    • 反身的: p与p本身是连接的.
    • 对称的: 如果p连接到q,那么q也链接到p
    • 传递的: 如果p连接到q并且q连接到r,那么p连接到r.

    在这里插入图片描述

    连接的组成

    在这里插入图片描述

    Union-find数据类型(API)

    在这里插入图片描述
    目标:为union-find设计有效的数据结构

    • 对象N的数量可能是巨大的
    • 操作次数M可能是巨大的
    • 查找操作和union链接操作可能是混合在一起的
    type UF struct
    func (uf UF)NewUF(N int) 使用N个对象(0-N-1)初始化union-find数据结构
    func union(p, q int) 在p和q之间添加连接
    func connected(p, q int) 判断p和q是否相连
    
    • 1
    • 2
    • 3
    • 4

    动态连接客户端

    • 从标准输入stdin中读取输入
    • 重复:
      1.从标准输入获取整数对
      2.如果他们不相连, 连接他们并打印这一对数字
    package main
    
    import (
    	"bufio"
    	"os"
    	"strconv"
    	"strings"
    
    	"github.com/iqer/algo4_go/algs4"
    )
    
    func main() {
    	scanner := bufio.NewScanner(os.Stdin)
    	scanner.Scan()
    
    	n, _ := strconv.Atoi(scanner.Text())
    	uf := algs4.NewUF(n)
    	for scanner.Scan() {
    		line := scanner.Text()
    		values := strings.Split(line, " ")
    		p, _ := strconv.Atoi(values[0])
    		q, _ := strconv.Atoi(values[1])
    		uf.Union(p, q)
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    quick-find 一种贪心算法

    在这里插入图片描述
    不同的点只有当在数组中的项是一样的时候, 那么他们是连通的.
    校验p和q是否有相同的id
    在这里插入图片描述

    quick-find的实现在这里插入图片描述

    func NewUF(n int) *UF {
    	uf := UF{n: n}
    	uf.id = make([]int, n)
    	// 将读取到的数字存在于一个数组中
    	// 每个索引就代表着一个数
    	// 索引对应的值就是连通的点的索引值
    	// 之后如果不同索引位置上的值相同, 就代表这些索引位置代表的点是连通的
    	for i := 0; i < n; i++ {
    		uf.id[i] = i
    	}
    	return &uf
    }
    
    func (uf *UF) connected(p, q int) bool {
    	return uf.id[p] == uf.id[q]
    }
    
    func (uf *UF) Union(p, q int) {
    	pid := uf.id[p]
    	qid := uf.id[q]
    	for i := 0; i < len(uf.id); i++ {
    		if uf.id[i] == pid {
    			uf.id[i] = qid
    		}
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    分析算法效率

    算法初始化union操作find查找操作
    快速查找O(N)O(N)1

    尝试一种快速合并的算法

    一种’懒的方法’, 尽量避免计算直到不得不进行计算
    在这里插入图片描述
    依然使用数组, 不过将其看作一组树即一片森林的表示.
    Find查找操作就变成寻找两者是否拥有同样的parent节点, 如果有就是相连的.
    这样做调整时, 只需要将p的根节点指向q的根节点, 就可以了.

    func (uf *UF) root(i int) int {
    	for i != uf.id[i] {
    		i = uf.id[i]
    	}
    	return i
    }
    
    func (uf *UF) connected(p, q int) bool {
    	return uf.id[p] == uf.id[q]
    }
    
    func (uf *UF) Union(p, q int)  {
    	i := uf.root(p)
    	j := uf.root(q)
    	uf.id[i] = j
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    分析对比一下 quick-union还是太慢了

    算法初始化union操作find查找操作
    quick-findO(N)O(N)1
    quick-uniono(N)O(N)O(N)

    quick-find劣势

    • Union操作代价高昂(N维路径)
    • 树时平的, 但维持他们保持平的代价很高

    quick-union劣势

    • 树变高了
    • 查找操作复杂度很高(可能是N维操作)

    对quick-union的改进

    改进方式1: 权重

    在这里插入图片描述
    避免将更大的树放在低位
    在这里插入图片描述
    上图对比普通的quick-union操作和带权重的union操作, 可以看到带权重的操作对树高有所控制.

    package algs4
    
    type UF struct {
    	id   []int
    	n    int
    	size map[int]int
    }
    
    func NewUF(n int) *UF {
    	uf := UF{n: n}
    	uf.id = make([]int, n)
    	uf.size = map[int]int{}
    	// 将读取到的数字存在于一个数组中
    	// 每个索引就代表着一个数
    	// 索引对应的值就是连通的点的索引值
    	// 之后如果不同索引位置上的值相同, 就代表这些索引位置代表的点是连通的
    	for i := 0; i < n; i++ {
    		uf.id[i] = i
    		uf.size[i] = 1
    	}
    	return &uf
    }
    
    func (uf *UF) root(i int) int {
    	for i != uf.id[i] {
    		i = uf.id[i]
    	}
    	return i
    }
    
    func (uf *UF) connected(p, q int) bool {
    	return uf.root(p) == uf.root(q)
    }
    
    // improved quick-union
    
    func (uf *UF) Union(p, q int) {
    	i := uf.root(p)
    	j := uf.root(q)
    	if i == j {
    		return
    	}
    	if uf.size[i] < uf.size[j] {
    		uf.id[i] = j
    		uf.size[j] += uf.size[i]
    	} else {
    		uf.id[j] = i
    		uf.size[i] += uf.size[j]
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    在这里插入图片描述
    树高在logN

    分析带权重的quick-union

    算法初始化union操作find查找操作
    quick-findO(N)O(N)1
    quick-uniono(N)O(N)O(N)
    带权重的QUNlogNlogN

    继续的改进方式2: path compression路径压缩

    func (uf *UF) root(i int) int {
    	for i != uf.id[i] {
    		// 路径压缩, 使得每一个节点挂到它的祖父辈节点下
    		uf.id[i] = uf.id[uf.id[i]]
    		i = uf.id[i]
    	}
    	return i
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    算法性能对比

    算法最差情况耗时
    quick-findMN
    quick-unionMN
    weighted QUN+MlogN
    QU+path compressionN+MlogN
    weighted QU + path compressionN+MlogN

    Union-Find的应用场景

    在这里插入图片描述

  • 相关阅读:
    传输层 --- UDP
    开源安全的危机在于太相信 GitHub?——专访Apache之父&OpenSSF基金会总经理Brain Behlendorf...
    智能座舱系列一:智能化基础平台及架构
    深度学习之人脸检测算法
    神奇的python的生成器
    LabVIEW编程LabVIEW开发SMP10辐射表例程与相关资料
    采购管控: 缩短采购周期,从源头上降本增效
    【德哥说库系列】-PostgreSQL跨版本升级
    嵌入式Ubuntu安装Opencv
    各种生成模型:VAE、GAN、flow、DDPM、autoregressive models
  • 原文地址:https://blog.csdn.net/weixin_43547795/article/details/130879598