• java实现并查集


    介绍

    并查集是一种特殊的树结构,示例图如下

    可以很方便的进行以下两种操作:以上图为例

    • 判断元素6和元素4是否属于同一组,
    • 合并元素6和元素4所在的组

    代码实现

    public interface UF {
    
      /**
       * 容量
       */
      int size();
    
      /**
       * 是否已连接
       */
      boolean connected(int p, int q);
    
      /**
       * 合并
       */
      void union(int p, int q);
    
    }
    

    定义接口

    public class UnionFind implements UF {
    
      private Node[] data;
    
      public UnionFind(int size) {
        data = new Node[size];
        for (int i = 0; i < data.length; i++) {
          //默认每个节点都指向自己
          data[i] = new Node(i);
        }
      }
    
      @Override
      public boolean connected(int p, int q) {
        rangeCheck(p, q);
        return root(p) == root(q);
      }
    
      /**
       * 查询p指向的根节点
       */
      private int root(int p) {
        int cur = p;
        while (true) {
          int parent = data[cur].parent;
          if (parent == cur) {
            return cur;
          }
          cur = parent;
        }
      }
    
      @Override
      public void union(int p, int q) {
        rangeCheck(p, q);
        int pRoot = root(p);
        int qRoot = root(q);
        if (pRoot == qRoot) {
          return;
        }
        data[qRoot].parent = pRoot;
      }
    
      public int size() {
        return data.length;
      }
    
      private void rangeCheck(int p, int q) {
        if (p < 0 || p >= size() || q < 0 || q >= size()) {
          throw new IllegalArgumentException("index is illegal");
        }
      }
    
      private static class Node {
    
        int parent;
    
        Node(int parent) {
          this.parent = parent;
        }
      }
    }
    

    测试代码

    public class Main {
    
      public static void main(String[] args) {
        UF uf = new UnionFind(10);
        uf.union(4, 3);
        uf.union(3, 8);
        uf.union(6, 5);
        uf.union(9, 4);
        uf.union(2, 1);
        uf.union(5, 0);
        uf.union(7, 2);
        uf.union(6, 2);
        System.out.println(uf.connected(0, 2));
        System.out.println(uf.connected(0, 7));
        System.out.println(uf.connected(3, 9));
        System.out.println(uf.connected(6, 4));
      }
    
    }
    

    初始化之后的树结构为

    每一个父节点都指向自己,经过以上的合并之后的树为

    判断两个元素是否连接就是判断两个元素的根节点是否一致。

    基于深度的优化

    上面的代码虽然功能实现了,但可能出现子链很长的情况,这种情况查询就很低效率。

    以上图为例,当我们要合并元素3和元素4的时候,我们应该使用后一种合并方式,将深度小的合并到深度大的。

    public class UnionFind2 implements UF {
    
      private Node[] data;
    
      public UnionFind2(int size) {
        data = new Node[size];
        for (int i = 0; i < data.length; i++) {
          data[i] = new Node(i, 1);
        }
      }
    
      @Override
      public boolean connected(int p, int q) {
        rangeCheck(p, q);
        return root(p) == root(q);
      }
    
      private int root(int p) {
        int cur = p;
        while (true) {
          int parent = data[cur].parent;
          if (parent == cur) {
            return cur;
          }
          cur = parent;
        }
      }
    
      @Override
      public void union(int p, int q) {
        rangeCheck(p, q);
        int pRoot = root(p);
        int qRoot = root(q);
        if (pRoot == qRoot) {
          return;
        }
        //深度小的指向深度大的
        if (data[pRoot].depth < data[qRoot].depth) {
          data[pRoot].parent = qRoot;
        } else if (data[pRoot].depth > data[qRoot].depth) {
          data[qRoot].parent = pRoot;
        } else {
          data[pRoot].depth += 1;
        }
      }
    
      public int size() {
        return data.length;
      }
    
      private void rangeCheck(int p, int q) {
        if (p < 0 || p >= size() || q < 0 || q >= size()) {
          throw new IllegalArgumentException("index is illegal");
        }
      }
    
      private static class Node {
    
        int parent;
        int depth;
    
        Node(int parent, int depth) {
          this.parent = parent;
          this.depth = depth;
        }
      }
    }
    

    路径压缩

    在查询的时候我们也可以进行优化

    private int root(int p) {
        int parent = data[p].parent;
        if (parent == p) {
          return p;
        }
        return data[p].parent = root(parent);
      }
    

    以查询元素4为例,直接将元素4的父节点指向根节点0。

  • 相关阅读:
    「Vijos 1282」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔法照片
    「Vijos 1285」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔法药水
    「Vijos 1284」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔法阵
    「Vijos 1283」「OIBH杯NOIP2006第二次模拟赛」佳佳的魔杖
    「2018-12-02模拟赛」T3 约束排列 解题报告
    「2018-12-02模拟赛」T2 种树 解题报告
    「2018-12-02模拟赛」T1 最短路 解题报告
    「分块系列」公主的朋友 解题报告
    「分块系列」「洛谷P4168 [Violet]」蒲公英 解题报告
    Java高级架构师(一)第03节:多模块多Web应用合并War包
  • 原文地址:https://www.cnblogs.com/strongmore/p/14231868.html
Copyright © 2020-2023  润新知