前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >傻子都能看懂的并查集算法

傻子都能看懂的并查集算法

作者头像
xujjj
发布2020-05-18 14:48:52
4630
发布2020-05-18 14:48:52
举报
文章被收录于专栏:IT界的泥石流

一、定义

其实并查集顾名思义就是有“合并集合(Union)”和“查找两个元素是否在同一集合(isSameSet)”两种操作的关于数据结构的一种算法。举个例子。如下图

有a、b、c、d、e五个元素,每个元素处于自己的集合中,比如a元素所在的集合只有a这么一个元素,其他也类似。

然后我们定义每个元素的父亲指针指向自己,如下图所示。

二、并查集的合并操作(Union方法)

例如,我们想要将a所在的集合和b所在的集合合并起来。我们首先通过a的父亲指针一直往上找,找到a的最顶部的根节点,即为a然后同样通过b的父亲指针一直往上找,找到b的最顶部的根节点,即为b最后将集合元素较少的根节点挂到集合元素较大的根节点,该例子中是b和a两个集合的元素都为1,谁挂谁都可以,假如是b挂a,结果如下图。

此时,如果再执行Union(b,c)这一操作,即合并b所在的集合和c所在的集合。同样,通过b的父亲指针一直往上找,找到b的最顶部的根节点,即为a,然后同样通过c的父亲指针一直往上找,找到c的最顶部的根节点,即为c。然后把c挂到a上面。如下图。

此时,可以看到,a、b、c三个元素属于同一集合了,而d和e还是属于各自的两个不同的集合。

三、并查集的查找两个元素是否在同一集合操作(isSameSet方法)

现在我们想要执行isSameSet(b,c)方法,即判断b和c是否属于同一集合,同样的,我们可以通过b的父亲指针一直往上找,找到b的最顶部的根节点,为a。然后通过c的父亲指针一直往上找,找到c的最顶部的根节点,也为a。此时我们就可以知道,两者的根节点相等,即b和c属于同一集合。

四、如何加速整个算法?

在上面的两个操作中,我们都涉及了同样的一个操作,即找到某个元素的根节点元素,我们暂且定义该操作为findHead操作,那么一直往上找的这个遍历过程我们可以做如下优化。比如,并查集的某一状态如下图所示。

当我们执行Union或者isSameSet操作时候,都会涉及这个findHead操作,比如,执行isSameSet(e,c)的时候,即判断e和c是否属于同一集合,那么我们首先要找到e的根节点,在往上找的过程中,我们可以记录找寻的路径,即会经过e、c、b、a,然后把这些经过的元素的父亲指针直接指向根节点a。(事实上,只需要改变e、c节点的父亲指针指向即可)。那么下次在找c的根节点的时候,就无需再遍历了。

通过该优化后,可以使得并查集的Union和isSameSet两个操作的时间复杂度均为O(1).

五、参考代码

代码语言:txt
复制
class Element {   //1---->元素1.仅仅是包一层的意思。
    public int value;

    public Element(int value) {
      this.value = value;
    } 
  }
  
public static class UnionFindSet{
    public HashMap<Integer,Element> elementMap;
    // key  某个元素  value 该元素的父
    public HashMap<Element, Element> fatherMap;
    // key 某个集合的代表元素   value 该集合的大小
    public HashMap<Element, Integer> sizeMap;
    
    public UnionFindSet(ArrayList<Integer> arrList) {
      elementMap = new HashMap<>();
      fatherMap = new HashMap<>();
      sizeMap = new HashMap<>();
      
      for(Integer item:arrList) {
        Element ele = new Element(item);
        elementMap.put(item, ele);
        fatherMap.put(ele, ele);
        sizeMap.put(ele, 1);
      }
    }
    public Element findHead(Element ele) {
      Stack<Element> stack = new Stack();
      
      while(ele != fatherMap.get(ele)) {
        stack.add(ele);
        ele = fatherMap.get(ele);
      }
      
      while(!stack.isEmpty()) {
        fatherMap.put(stack.pop(), ele);
      }
      
      return ele;
    }
    
    public boolean isSameSet(Integer a, Integer b) {
      if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
        return findHead(elementMap.get(a)) == findHead(elementMap.get(b));
      }
      return false;
    }
    public void union(Integer a, Integer b) {
      if (elementMap.containsKey(a) && elementMap.containsKey(b)) {
        Element aF = findHead(elementMap.get(a));
        Element bF = findHead(elementMap.get(b));
        if (aF != bF) {
          Element big = sizeMap.get(aF) >= sizeMap.get(bF) ? aF : bF;
          Element small = big == aF ? bF : aF;
          fatherMap.put(small, big);
          sizeMap.put(big, sizeMap.get(aF) + sizeMap.get(bF));
          sizeMap.remove(small);
        }
      }
    } 
  }
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-03-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 IT界的泥石流 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档