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;
    }
    }
    }

第三十一篇 玩转数据结构——并查集(Union Find)的更多相关文章

  1. 第三十三篇 玩转数据结构——红黑树(Read Black Tree)

    1.. 图解2-3树维持绝对平衡的原理: 2.. 红黑树与2-3树是等价的 3.. 红黑树的特点 简要概括如下: 所有节点非黑即红:根节点为黑:NULL节点为黑:红节点孩子为黑:黑平衡 4.. 实现红 ...

  2. 第三十篇 玩转数据结构——字典树(Trie)

          1.. Trie通常被称为"字典树"或"前缀树" Trie的形象化描述如下图: Trie的优势和适用场景 2.. 实现Trie 实现Trie的业务无 ...

  3. 算法手记 之 数据结构(并查集详解)(POJ1703)

    <ACM/ICPC算法训练教程>读书笔记-这一次补上并查集的部分.将对并查集的思想进行详细阐述,并附上本人AC掉POJ1703的Code. 在一些有N个元素的集合应用问题中,通常会将每个元 ...

  4. ACM数据结构-并查集

    ACM数据结构-并查集   并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合 ...

  5. 【题解】P2024 [NOI2001]食物链 - 数据结构 - 并查集

    P2024 [NOI2001]食物链 声明:本博客所有题解都参照了网络资料或其他博客,仅为博主想加深理解而写,如有疑问欢迎与博主讨论✧。٩(ˊᗜˋ)و✧*。 题目描述 动物王国中有三类动物 \(A,B ...

  6. 图论&数据结构——并查集

    Wikioi 4246 NOIP模拟赛Day2T1 奶牛的身高  题目描述 Description 奶牛们在FJ的养育下茁壮成长.这天,FJ给了奶牛Bessie一个任务,去看看每个奶牛场中若干只奶牛的 ...

  7. 《挑战程序设计竞赛》2.4 数据结构-并查集 POJ1182 2236 1703 AOJ2170

    POJ1182 http://poj.org/problem?id=1182 题目 难得的中文题... 食物链 Time Limit: 1000MS Memory Limit: 10000K Tota ...

  8. 第三十四篇 玩转数据结构——哈希表(HashTable)

    1.. 整型哈希函数的设计 小范围正整数直接使用 小范围负整数整体进行偏移 大整数,通常做法是"模一个素数"   2.. 浮点型哈希函数的设计 转成整型进行处理   3.. 字符串 ...

  9. 第三十二篇 玩转数据结构——AVL树(AVL Tree)

          1.. 平衡二叉树 平衡二叉树要求,对于任意一个节点,左子树和右子树的高度差不能超过1. 平衡二叉树的高度和节点数量之间的关系也是O(logn) 为二叉树标注节点高度并计算平衡因子 AVL ...

随机推荐

  1. how to use bookdown

    模板下载 {#moban} 谢益辉 通用模板:https://github.com/rstudio/bookdown-demo 李东风中文模板: http://www.math.pku.edu.cn/ ...

  2. 解决NahimicSvc32.exe与bilibili直播姬的音频不兼容的问题

    某次测试哔哩哔哩直播姬的时候发现系统声音采集异常的错误 NahimicSvc32.exe是NahimicService下的程序,奇怪的是我的本本所有硬件没有一个微星有关系,怎么就装上了微星的服务程序? ...

  3. BZOJ1040: [ZJOI2008]骑士 树套环DP

    题意:一个图n个点n条边保证点能互相到达,ab有边意味着ab互相厌恶,求一个集合,使得集合里元素最多而且没有人互相厌恶 删去环上一条边树形dp,比如删掉的边连着a,b,那么先dp出不选a的最大值,再d ...

  4. thinkphp3.2短信群发项目实例

    项目功能是企业给客户群发短信,我就写这么多,也不知道你能不能运行成功,如果有问题可以在QQ上问我:605114821 项目文件SMS_V2.zip下载地址,百度云:http://yun.baidu.c ...

  5. CentOS 7在执行yum操作时 报错

    CentOS 7在执行yum操作时, 报错:Could not retrieve mirrorlist http://mirrorlist.centos.org/?release=6&arch ...

  6. 2017-9-15Opencv 杂

    Mat::at()的具体含义.指的是三通道.(0),(1),(2)分别表示BGR: Vector<Mat>结构的使用.将Mat类型的数据转化成了具有多个单通道的容器? 灰度图的具体含义.和 ...

  7. git内容补充-Git零基础快速入门-苏玲

    https://git-scm.com/book/zh/v2 git历史 集中式版本控制管理:cvs.svn 分布式版本控制管理:git 基本命令 git config --list --global ...

  8. Luogu1287 | 盒子与球 (排列组合)

    贴一个和其他题解不一样的做法 QWQ 题意:让我们求出 N 个球放入 R 个盒子且每个盒子都必须放球方案数. 首先,对于每一个球,可以将其放入的盒子数量共有 R 个,所以我们可以知道如果无需满足每个盒 ...

  9. 软件架构期末复习(Struts2+Spring+Hibernate)

    Struts2+Spring+Hibernate The Model-ViewController pattern in Struts2 is implemented with the followi ...

  10. Gin_渲染

    1. 各种数据响应格式 package main import ( "github.com/gin-gonic/gin" "github.com/gin-gonic/gi ...