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. python3-cookbook笔记:第三章 数字日期和时间

    python3-cookbook中每个小节以问题.解决方案和讨论三个部分探讨了Python3在某类问题中的最优解决方式,或者说是探讨Python3本身的数据结构.函数.类等特性在某类问题上如何更好地使 ...

  2. Java出现次数最多的整数

    描述 编写一个程序,读入一组整数,这组整数是按照从小到大的顺序排列的,它们的个数N也是由用户输入的,最多不会超过20.然后程序将对这个数组进行统计,把出现次数最多的那个数组元素值打印出来.如果有两个元 ...

  3. tensor的维度扩张的手段--Broadcasting

    broadcasting是tensorflow中tensor维度扩张的最常用的手段,指对某一个维度上重复N多次,虽然它呈现数据已被扩张,但不会复制数据. 可以这样理解,对 [b,784]@[784,1 ...

  4. 获取redis cluster中所有节点的内存使用情况

    需求:获取redis cluster集群中所有节点的内存使用情况. ip_port=`redis-cli -h $ -p $ -a abc123 -c cluster nodes | awk '{pr ...

  5. Nginx模块之ngx_http_proxy_module

    ngx_http_proxy_module模块: 示例: location / { proxy_pass http://localhost:8000; proxy_set_header Host $h ...

  6. win10中安装与配置maven

    原文链接:https://www.cnblogs.com/wkrbky/p/6350334.html Maven安装配置(Windows10) 想要安装 Apache Maven 在Windows 系 ...

  7. python3练习100题——004

    继续做题-经过python3的测试 原题链接:http://www.runoob.com/python/python-exercise-example4.html 题目:输入某年某月某日,判断这一天是 ...

  8. react-父子子孙组件嵌套-context

    方案一 import React from 'react' import ReactTypes from 'prop-types' /* // 最外层的父组件 export default class ...

  9. yii2 环境切换(开发,正式)

    方式一,在web中index修改 开发环境配置 web目录index.php defined('YII_DEBUG') or define('YII_DEBUG', true); defined('Y ...

  10. ajax从jsp向servlet传值

    function Delete(s){ xmlHttp=new XMLHttpRequest(); var url = "/emp/FindSpecial?selcol=" +s; ...