09 并查集 UF_Tree算法优化 路径压缩 案例-畅通工程

目录

并查集结构

概念

并查集API设计

并查集实现

代码

UF_Tree算法优化

概念

API设计

代码

优化后的性能分析

优化前

优化后

路径压缩

概念

作用

代码

案例-畅通工程

代码


  1. 并查集结构

    1. 概念

      1. 合并与查询
  2. 并查集API设计

    1. 09 并查集 UF_Tree算法优化 路径压缩 案例-畅通工程_第1张图片
  3. 并查集实现

    1. 代码

      1. package lq.uf;
        
        public class UF {
            //记录结点元素和该元素所在分组的标识
            private int[] eleAndGroup;
            //记录并查集中数据的分组个数
            private int count;
        
            public int[] getEleAndGroup() {
                return eleAndGroup;
            }
        
            //初始化并查集
            public UF(int N) {
                //初始化分组个数
                count = N;
                //初始化eleAndGroup数组
                eleAndGroup = new int[N];
                //初始化数组元素
                for (int i = 0; i < N; i++) {
                    eleAndGroup[i] = i;
                }
            }
        
            //获取当前并查集中的数据有多少个分组
            public int count() {
                return count;
            }
        
            //元素p所在分组的标识符
            public int find(int p) {
                return eleAndGroup[p];
            }
        
            //判断并查集中元素p和元素q是否在同一分组中
            public boolean connected(int p, int q) {
                return eleAndGroup[p] == eleAndGroup[q];
            }
        
            //把p元素所在分组和q元素所在分组合并
            public void union(int p, int q) {
                if (connected(p, q)) {
                    return;
                }
                int pGroup = eleAndGroup[p];
                int qGroup = eleAndGroup[q];
                for (int i = 0; i < eleAndGroup.length; i++) {
                    if (eleAndGroup[i] == pGroup) {
                        eleAndGroup[i] = qGroup;
                    }
                }
                count--;
            }
        }
        

         

  4. UF_Tree算法优化

    1. 概念

      1. 如果一个网络中有n台计算机,假如我们想要n台计算机连接起来就需要调用n次union方法,union方法又要做一次循环,下来时间复杂度为O(n^2),还是挺慢的
    2. API设计

      1. 和之前的一模一样
    3. 代码

      1. package lq.uf;
        
        public class UF_Tree {
            //记录结点元素和该元素的父节点
            private int[] eleAndGroup;
            //记录并查集中数据的分组个数
            private int count;
        
            public int[] getEleAndGroup() {
                return eleAndGroup;
            }
        
            //初始化并查集
            public UF_Tree(int N) {
                //初始化分组个数
                count = N;
                //初始化eleAndGroup数组
                eleAndGroup = new int[N];
                /*
                初始化数组元素
                    初始化时, 默认每一个元素就是一个单独的分组, 所以可以说这个元素就是这个分组的父节点,
                    所以初始化时, eleAndGroup[i] = i
                 */
                for (int i = 0; i < N; i++) {
                    eleAndGroup[i] = i;
                }
            }
        
            //获取当前并查集中的数据有多少个分组
            public int count() {
                return count;
            }
        
            //判断并查集中元素p和元素q是否在同一分组中
        
            public boolean connected(int p, int q) {
                return find(p) == find(q);
            }
        
            //元素p所在分组的标识符
            public int find(int p) {
                while (true) {
                    if (p == eleAndGroup[p]) {
                        return p;
                    }
                    p = eleAndGroup[p];
                }
            }
        
            //把p元素所在分组和q元素所在分组合并
            public void union(int p, int q) {
                int pGroup = find(p);
                int qGroup = find(q);
                if (pGroup == qGroup) {
                    return;
                }
                eleAndGroup[pGroup] = qGroup;
                count--;
            }
        }
        

         

    4. 优化后的性能分析

      1. 优化前

        1. find快
        2. union慢
          1. 时间复杂度是O(n)
      2. 优化后

        1. find慢
          1. 最快情况下时间复杂度是O(n),所以我们依旧需要优化最坏情况下的时间复杂度
            1. 合并之后是一颗单链树,从上到下一条链
        2. union快
  5. 路径压缩

    1. 概念

      1. 改进union方法,将元素个数少的分组加入到元素个数多的分组,形成的新分组的树的高度是之前元素多的分组,这样可以不增加树的高度,进而提高查询时的效率
    2. 作用

      1. 降低UFTree中find方法最坏情况下的时间复杂度
    3. 代码

      1. package lq.uf;
        
        public class UFTreeWeighted {
            //记录结点元素和该元素的父节点
            private int[] eleAndGroup;
            //记录并查集中数据的分组个数
            private int count;
            //存储每一个结点所在组的元素个数
            private int[] sz;
        
            public int getCount() {
                return count;
            }
        
            public int[] getEleAndGroup() {
                return eleAndGroup;
            }
        
            //初始化并查集
            public UFTreeWeighted(int N) {
                //初始化分组个数
                count = N;
                //初始化eleAndGroup数组
                eleAndGroup = new int[N];
                sz = new int[N];
                /*
                初始化数组元素
                    初始化时, 默认每一个元素就是一个单独的分组, 所以可以说这个元素就是这个分组的父节点,
                    所以初始化时, eleAndGroup[i] = i
                 */
                for (int i = 0; i < N; i++) {
                    eleAndGroup[i] = i;
                    sz[i] = 1;
                }
            }
        
            //获取当前并查集中的数据有多少个分组
            public int count() {
                return count;
            }
        
            //判断并查集中元素p和元素q是否在同一分组中
        
            public boolean connected(int p, int q) {
                return find(p) == find(q);
            }
        
            //元素p所在分组的标识符
            public int find(int p) {
                while (true) {
                    if (p == eleAndGroup[p]) {
                        return p;
                    }
                    p = eleAndGroup[p];
                }
            }
        
            /**
             * 把p元素所在分组和q元素所在分组合并
             * 这一种合并方式可以降低查找的时间复杂度
             * 因为整体树的高度降低了
             *
             * @param p
             * @param q
             */
            public void union(int p, int q) {
                int pGroup = find(p);
                int qGroup = find(q);
                if (pGroup == qGroup) {
                    return;
                }
                if (sz[pGroup] > sz[qGroup]) {
                    eleAndGroup[qGroup] = pGroup;
                    sz[pGroup] += sz[qGroup];
                } else {
                    eleAndGroup[pGroup] = qGroup;
                    sz[qGroup] += sz[pGroup];
                }
                count--;
            }
        }
        

         

  6. 案例-畅通工程

    1. 代码

      1. package lq.test;
        
        import lq.uf.UFTreeWeighted;
        
        import java.io.BufferedReader;
        import java.io.InputStreamReader;
        
        /**
         * @author LQ
         * @create 2020-06-01 17:37
         */
        public class TrafficProjectTest {
            public static void main(String[] args) throws Exception {
                //创建一个缓冲输入流
                BufferedReader reader = new BufferedReader(new InputStreamReader(TrafficProjectTest.class.getClassLoader().getResourceAsStream("traffic_project.txt")));
                //读取第一行, 获取并查集大小
                int totalNumber = Integer.parseInt(reader.readLine());
                //创建并查集
                UFTreeWeighted ufTreeWeighted = new UFTreeWeighted(totalNumber);
                //读取第二行, 获取已经修好的路数k
                int k = Integer.parseInt(reader.readLine());
                //遍历k, 调用union方法
                for (int i = 0; i < k; i++) {
                    String s = reader.readLine();
                    String[] split = s.split(" ");
                    int p = Integer.parseInt(split[0]);
                    int q = Integer.parseInt(split[1]);
                    ufTreeWeighted.union(p, q);
                }
                //获取 当前分组数-1 就是还需要修的路的数量
                int last = ufTreeWeighted.getCount() - 1;
                System.out.println("last = " + last);
            }
        }
        

         

 

你可能感兴趣的:(数据结构与算法)