10.并查集结构

简介: 10.并查集结构

     

并查集

增删改查时间复杂度为O(1)的结构目前有哈希表跟并查集

  • 1)有若干个样本a、b、c、d…类型假设是V
  • 2)在并查集中一开始认为每个样本都在单独的集合里
  • 3)用户可以在任何时候调用如下两个方法:
  • boolean isSameSet(V x, V y):查询样本x和样本y是否属于一个集合
  • void union(V x, V y) :把x和y各自所在集合的所有样本合并成一个集合
  • 4)isSameSet和union方法的代价越低越好
  • 1)每个节点都有一条往上指的指针
  • 2)节点a往.上找到的头节点,叫做a所在集合的代表节点
  • 3)查询x和y是否属于同一个集合,就是看看找到的代表节点是不是一个
  • 4)把x和y各自所在集合的所有点合并成一个集合,只需要小集合的代表点挂
    在大集合的代表点的下方即可
并查集的优化
  • 1)节点往上找代表点的过程,把沿途的链变成扁平的
  • 2)小集合挂在大集合的下面
  • 3)如果方法调用很频繁,那么单次调用的代价为O(1),两个方法都如此
并查集的应用
  • 解决两大块区域的合并问题
  • 常用在图等领域中
并查集核心代码
package com.harrison.class10;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
public class Code01_UnionFind {
  public static class Node<V> {
    V value;
    public Node(V v) {
      value = v;
    }
  }
  public static class UnionFind<V> {
    public HashMap<V, Node<V>> nodes;
    public HashMap<Node<V>, Node<V>> parents;
    public HashMap<Node<V>, Integer> sizeMap;
    public UnionFind(List<V> values) {
      nodes = new HashMap<>();
      parents = new HashMap<>();
      sizeMap = new HashMap<>();
      for (V cur : values) {
        Node<V> node = new Node<>(cur);
        nodes.put(cur, node);
        parents.put(node, node);
        sizeMap.put(node, 1);
      }
    }
    public Node<V> findFather(Node<V> cur) {
      Stack<Node<V>> path = new Stack<>();
      while(cur!=parents.get(cur)) {
        path.push(cur);
        cur=parents.get(cur);
      }
      while(!path.isEmpty()) {
        parents.put(path.pop(),cur);
      }
      return cur;
    }
    public boolean isSameSet(V a,V b) {
      return findFather(nodes.get(a))==findFather(nodes.get(b));
    }
    public void union(V a, V b) {
      Node<V> aHead=findFather(nodes.get(a));
      Node<V> bHead=findFather(nodes.get(b));
      if(aHead!=bHead) {
        int aSetSize=sizeMap.get(aHead);
        int bSetSize=sizeMap.get(bHead);
          Node<V> big= aSetSize>=bSetSize?aHead:bHead;
          Node<V> small= big==aHead?bHead:aHead;
          parents.put(small, big);
          sizeMap.put(big, aSetSize+bSetSize);
          sizeMap.remove(small);
      }
    }
    public int sets() {
      return sizeMap.size();
    }
  }
}
【题目】

如果两个user,a字段一样、或者b字段一样、或者c字段一样,就认为是一个人。请合并users,返回合并之后的用户数量

package com.harrison.class10;
import java.util.HashMap;
import java.util.List;
import java.util.Stack;
public class Code02_MergeUsers {
  public static class User {
    public String a;
    public String b;
    public String c;
    public User(String a, String b, String c) {
      this.a = a;
      this.b = b;
      this.c = c;
    }
  }
  public static class Node<V> {
    V value;
    public Node(V v) {
      value = v;
    }
  }
  public static class UnionFind<V> {
    public HashMap<V, Node<V>> nodes;
    public HashMap<Node<V>, Node<V>> parents;
    public HashMap<Node<V>, Integer> sizeMap;
    public UnionFind(List<V> values) {
      nodes = new HashMap<>();
      parents = new HashMap<>();
      sizeMap = new HashMap<>();
      for (V cur : values) {
        Node<V> node = new Node<>(cur);
        nodes.put(cur, node);
        parents.put(node, node);
        sizeMap.put(node, 1);
      }
    }
    public Node<V> findFather(Node<V> cur) {
      Stack<Node<V>> path = new Stack<>();
      while (cur != parents.get(cur)) {
        path.push(cur);
        cur = parents.get(cur);
      }
      while (!path.isEmpty()) {
        parents.put(path.pop(), cur);
      }
      return cur;
    }
    public boolean isSameSet(V a, V b) {
      return findFather(nodes.get(a)) == findFather(nodes.get(b));
    }
    public void union(V a, V b) {
      Node<V> aHead = findFather(nodes.get(a));
      Node<V> bHead = findFather(nodes.get(b));
      if (aHead != bHead) {
        int aSetSize = sizeMap.get(aHead);
        int bSetSize = sizeMap.get(bHead);
        Node<V> big = aSetSize >= bSetSize ? aHead : bHead;
        Node<V> small = big == aHead ? bHead : aHead;
        parents.put(small, big);
        sizeMap.put(big, aSetSize + bSetSize);
        sizeMap.remove(small);
      }
    }
    public int getSizeNum() {
      return sizeMap.size();
    }
  }
  // 如果两个user,a字段一样、或者b字段一样、或者c字段一样,就认为是一个人
  // 请合并users,返回合并之后的用户数量
  public static int mergeUsers(List<User> users) {
    UnionFind<User> unionFind = new UnionFind<>(users);
    HashMap<String, User> mapA = new HashMap<>();
    HashMap<String, User> mapB = new HashMap<>();
    HashMap<String, User> mapC = new HashMap<>();
    for (User user : users) {
      if (mapA.containsKey(user.a)) {
        unionFind.union(user, mapA.get(user.a));
      }else {
        mapA.put(user.a, user);
      }
      if (mapB.containsKey(user.b)) {
        unionFind.union(user, mapB.get(user.b));
      }else {
        mapB.put(user.b, user);
      }
      if (mapC.containsKey(user.c)) {
        unionFind.union(user, mapC.get(user.c));
      }else {
        mapC.put(user.c, user);
      }
    }
    //向并查集询问,合并之后,还有多少个集合
    return unionFind.getSizeNum();
  }
}

           


相关文章
|
存储 机器学习/深度学习 人工智能
数据结构学习笔记——图的存储结构(邻接矩阵和邻接表)
数据结构学习笔记——图的存储结构(邻接矩阵和邻接表)
数据结构学习笔记——图的存储结构(邻接矩阵和邻接表)
|
存储 算法 C语言
【数据结构】前中后层序遍历 --二叉树的结构特点与遍历方式
与之前链表的Node类似,所以就不细讲了(给定一个值,malloc其节点,将值存入x,其左右指针置空,返回这个节点值)
79 0
|
C语言
【数据结构】二叉树的建立及先中后序遍历完整C语言代码
【数据结构】二叉树的建立及先中后序遍历完整C语言代码
351 0
|
存储
数据结构(8)树形结构——B树、B+树(含完整建树过程)
8.1.B树 8.1.1.概述 B树存在的意义: 二叉树在存储数据时可能出现向一边倾斜导致查询效率降低的情况,为了防止二叉树的倾斜,出现了平衡二叉树,通过旋转的方式保证二叉树的平衡。但是就算是保持绝对的平衡,在面对要存储的数量量级够大的时候也会出现树的高度整体偏高的问题,树的高度过高,即使是使用了二分查找,依然会出现查找效率变低的情况。尤其是磁盘查找数据本身是个机械完成的动作,这一动作本身就十分耗时。因此需要一种能进行二分查找缩短查找时间,能存储大量数据后树高也不会过高的树形结构,这就是B树。
211 0
数据结构之二叉树的结构和遍历的实现
数据结构之二叉树的结构和遍历的实现
|
存储 算法
【数据结构】图以及图的遍历(深度遍历和广度遍历)
【数据结构】图以及图的遍历(深度遍历和广度遍历)
167 0
数据结构193-图论-图的遍历方法
数据结构193-图论-图的遍历方法
65 0
数据结构193-图论-图的遍历方法
|
存储 算法
【数据结构oj】树的度(树和二叉树的相互转化)
【数据结构oj】树的度(树和二叉树的相互转化)
57 0
|
存储
大话数据结构--二叉树的性质
大话数据结构--二叉树的性质
70 0
数据结构191-图论-添加顶点边代码
数据结构191-图论-添加顶点边代码
53 0
数据结构191-图论-添加顶点边代码