1.. 并查集的应用场景
  • 查看"网络"中节点的连接状态,这里的网络是广义上的网络
  • 数学中的集合类的实现
 
2.. 并查集所支持的操作
  • 对于一组数据,并查集主要支持两种操作:合并两个数据、判断两个数据是否属于同一集合(两个数据是否连接)
 
3.. 定义并查集的接口
  • 并查集的接口业务逻辑如下:
  • public interface UF {
    
        int getSize();
    
        boolean isConnected(int p, int q);
    
        void unionElements(int p, int q);
    
    }
4.. 实现并查集
  • 第一版并查集Quick Find,业务逻辑如下:
  • public class UnionFind1 implements UF {
    
        private int[] id;
    
        public UnionFind1(int size) {
    
            id = new int[size];
    for (int i = 0; i < id.length; i++) {
    id[i] = i;
    }
    } // 实现getSize方法
    @Override
    public int getSize() {
    return id.length;
    } private int find(int p) { if (p < 0 || p >= id.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    return id[p];
    } // 实现isConnected方法,查看元素p与元素q是否所属同一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return id[p] == id[q];
    } // 实现unionElements方法,合并元素p和元素q所属集合
    @Override
    public void unionElements(int p, int q){ int pID = find(p);
    int qID = find(q); if(pID == qID){
    return;
    }
    for(int i=0;i<id.length;i++){
    if(id[i]==pID){
    id[i] = qID;
    }
    }
    }
    }
  • Quick FInd的时间复杂度分析
  • 第二版并查集Quick Union,业务逻辑如下:
  • public class UnionFind2 implements UF {
    
        private int[] parent;
    
        public UnionFind2(int size) {
    
            parent = new int[size];
    for (int i = 0; i < size; i++) {
    parent[i] = i;
    }
    } @Override
    public int getSize() {
    return parent.length;
    } private int find(int p) { if (p < 0 || p >= parent.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    while (p != parent[p]) {
    p = parent[p];
    }
    return p;
    } // 实现isConnected方法,判断元素p与元素q是否同属一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return parent[p] == parent[q];
    } // 实现unionElements方法,合并元素p和元素q所在集合
    @Override
    public void unionElements(int p, int q) { int pRoot = find(p);
    int qRoot = find(q); if (pRoot == qRoot) {
    return;
    }
    parent[pRoot] = qRoot;
    }
    }
  • Quick Union的时间复杂度分析
  • isConnected     O(h)   其中,h为树的高度
    unionElements O(h)
  • 测试Quick Find和Quick Union的性能
  • 测试的业务逻辑如下:
  • import java.util.Random;
    
    public class Main {
    
        private static double testUF(UF uf, int m) {
    
            int size = uf.getSize();
    Random random = new Random();
    long startTime = System.nanoTime(); for (int i = 0; i < m; i++) {
    int a = random.nextInt(size);
    int b = random.nextInt(size);
    uf.unionElements(a, b);
    } for (int i = 0; i < m; i++) {
    int a = random.nextInt(size);
    int b = random.nextInt(size);
    uf.isConnected(a, b);
    } long endTime = System.nanoTime();
    return (endTime - startTime) / 1000000000.0;
    } public static void main(String[] args) {
    int size = 100000;
    int m = 10000; UnionFind1 uf1 = new UnionFind1(size);
    double time1 = testUF(uf1, m);
    System.out.println("Quick Find, time: " + time1 + " s"); UnionFind2 uf2 = new UnionFind2(size);
    double time2 = testUF(uf2, m);
    System.out.println("Quick Union, time: " + time2 + " s");
    }
    }
  • 输出结果:
  • Quick Find, time: 0.272248873 s
    Quick Union, time: 0.001273318 s
5.. Quick Union的优化
  • 对unionElements方法进行优化,使元素少的节点指向元素多的节点
  • 优化后的业务逻辑如下:
  • public class UnionFind3 implements UF {
    
        private int[] parent;
    private int[] sz; public UnionFind3(int size) { parent = new int[size];
    sz = new int[size]; for (int i = 0; i < size; i++) {
    parent[i] = i;
    sz[i] = 1;
    }
    } @Override
    public int getSize() {
    return parent.length;
    } private int find(int p) { if (p < 0 || p >= parent.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    while (p != parent[p]) {
    p = parent[p];
    }
    return p;
    } // 实现isConnected方法,判断元素p与元素q是否同属一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return parent[p] == parent[q];
    } // 实现unionElements方法,合并元素p和元素q所在集合
    @Override
    public void unionElements(int p, int q) { int pRoot = find(p);
    int qRoot = find(q); if (pRoot == qRoot) {
    return;
    }
    if (sz[pRoot] < sz[qRoot]) {
    parent[pRoot] = qRoot;
    sz[qRoot] += sz[pRoot];
    }else{
    parent[qRoot] = pRoot;
    sz[pRoot]+=sz[qRoot];
    }
    }
    }
  • 对unionElements方法进行优化,使深度浅节点指向深度更深的节点
  • 优化后的业务逻辑如下:
  • public class UnionFind4 implements UF {
    
        private int[] parent;
    private int[] rank; public UnionFind4(int size) { parent = new int[size];
    rank = new int[size]; for (int i = 0; i < size; i++) {
    parent[i] = i;
    rank[i] = 1;
    }
    } @Override
    public int getSize() {
    return parent.length;
    } private int find(int p) { if (p < 0 || p >= parent.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    while (p != parent[p]) {
    p = parent[p];
    }
    return p;
    } // 实现isConnected方法,判断元素p与元素q是否同属一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return parent[p] == parent[q];
    } // 实现unionElements方法,合并元素p和元素q所在集合
    @Override
    public void unionElements(int p, int q) { int pRoot = find(p);
    int qRoot = find(q); if (pRoot == qRoot) {
    return;
    }
    if (rank[pRoot] < rank[qRoot]) {
    parent[pRoot] = qRoot;
    } else if (rank[qRoot] < rank[pRoot]) {
    parent[qRoot] = pRoot;
    } else {
    parent[pRoot] = qRoot;
    rank[qRoot] += 1;
    }
    }
    }
  • 对find方法进行优化,实现简单路径压缩(非递归实现)
  • 优化后业务逻辑如下
  • public class UnionFind5 implements UF {
    
        private int[] parent;
    private int[] rank; public UnionFind5(int size) { parent = new int[size];
    rank = new int[size]; for (int i = 0; i < size; i++) {
    parent[i] = i;
    rank[i] = 1;
    }
    } @Override
    public int getSize() {
    return parent.length;
    } private int find(int p) { if (p < 0 || p >= parent.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    while (p != parent[p]) {
    parent[p] = parent[parent[p]]; // 优化了这里
    p = parent[p];
    }
    return p;
    } // 实现isConnected方法,判断元素p与元素q是否同属一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return parent[p] == parent[q];
    } // 实现unionElements方法,合并元素p和元素q所在集合
    @Override
    public void unionElements(int p, int q) { int pRoot = find(p);
    int qRoot = find(q); if (pRoot == qRoot) {
    return;
    }
    if (rank[pRoot] < rank[qRoot]) {
    parent[pRoot] = qRoot;
    } else if (rank[qRoot] < rank[pRoot]) {
    parent[qRoot] = pRoot;
    } else {
    parent[pRoot] = qRoot;
    rank[qRoot] += 1;
    }
    }
    }
  • 再次优化find方法,实现终极路径压缩(递归实现)
  • public class UnionFind6 implements UF {
    
        private int[] parent;
    private int[] rank; public UnionFind6(int size) { parent = new int[size];
    rank = new int[size]; for (int i = 0; i < size; i++) {
    parent[i] = i;
    rank[i] = 1;
    }
    } @Override
    public int getSize() {
    return parent.length;
    } private int find(int p) { if (p < 0 || p >= parent.length) {
    throw new IllegalArgumentException("p is out of bound.");
    }
    if (p != parent[p]) {
    parent[p] = find(parent[p]); // 优化了这里
    }
    return parent[p];
    } // 实现isConnected方法,判断元素p与元素q是否同属一个集合
    @Override
    public boolean isConnected(int p, int q) {
    return parent[p] == parent[q];
    } // 实现unionElements方法,合并元素p和元素q所在集合
    @Override
    public void unionElements(int p, int q) { int pRoot = find(p);
    int qRoot = find(q); if (pRoot == qRoot) {
    return;
    }
    if (rank[pRoot] < rank[qRoot]) {
    parent[pRoot] = qRoot;
    } else if (rank[qRoot] < rank[pRoot]) {
    parent[qRoot] = pRoot;
    } else {
    parent[pRoot] = qRoot;
    rank[qRoot] += 1;
    }
    }
    }

最新文章

  1. selenium web driver 实现截图功能
  2. 自己写的几个android自定义组件
  3. 17.iOS App设置icon,启动图,App名称的方法
  4. [小技巧] shell 下查看串口是否工作正常
  5. Hadoop - 实时查询Drill
  6. 【linux】Cache和Buffer的区别
  7. UIView 添加子视图的常用方法
  8. BZOJ 4259 残缺的字符串(FFT)
  9. [算法] trie树实现
  10. 004.Create a web app with ASP.NET Core MVC using Visual Studio on Windows --【在 windows上用VS创建mvc web app】
  11. Hadoop分布式集群搭建hadoop2.6+Ubuntu16.04
  12. leetcode:程序猿面试技巧
  13. mpvue小程序开发之 iconfont图标引入
  14. VUE-009-页面打开时初始化配置项内容
  15. Linux下配置环境变量—— .bashrc 和 /etc/profile
  16. 嵌入式常用技术概览之SPI
  17. SNF开发平台WinForm-审核流使用方法样例
  18. SpringBoot集成redisson分布式锁
  19. 第8章—使用Spring Web Flow—Spring Web Flow的配置
  20. 并发编程(二)------并发类容器ConcurrentMap

热门文章

  1. 使用nginx代理gogs遇到推送代码错误的问题(RPC failed; HTTP 413 curl 22 The requested URL returned error: 413)
  2. vue登录管理
  3. PAT (Basic Level) Practice (中文)1022 D进制的A+B (20 分)
  4. Android_小账本小结
  5. 修改testlink上传文件大小
  6. PP: Meta-learning framework with applications to zero-shot time-series forecasting
  7. Qt Installer Framework翻译(8)
  8. rp算法 随机化 刷题记录
  9. luogu P2158 [SDOI2008]仪仗队 (欧拉函数)
  10. Git 版本回退的几种操作方法