并查集

并查集

并查集是一种树形的数据结构,用于处理一些不交集的合并查询的问题

  • 查找(Find):确定某个元素属于哪个集合
  • 合并(Union):将两个子集合成一个集合

理解

我们举个故事来理解并查集的思想:

几个家族进行宴会,但是家族普遍长寿,所以人数众多。由于长时间的分离以及年龄的增长,这些人逐渐忘掉了自己的亲人,只记得自己的爸爸是谁了,而最长者(称为「祖先」)的父亲已经去世,他只知道自己是祖先。为了确定自己是哪个家族,他们想出了一个办法,只要问自己的爸爸是不是祖先,一层一层的向上问,直到问到祖先。如果要判断两人是否在同一家族,只要看两人的祖先是不是同一人就可以了。

初始化

让每个点的祖宗为其自己

for (int i = 1;i <= n; i++) {
  p[i] = i;
}

寻找祖宗

//寻找x的祖先
int find(int x) {
  //如果x是祖先则先返回,如果不是则让x的爸爸去问x的爷爷
  if (p[x] == x) {
    return x;
  }else {
    return find(p[x]);
  }
}

优化-----路径压缩

通过find函数的确可以达成目的,但是显然效率实在太低。为什么呢?因为我们使用了太多没用的信息,我的祖先是谁与我父亲是谁没什么关系,这样一层一层找太浪费时间,不如我直接当祖先的儿子,问一次就可以出结果了。甚至祖先是谁都无所谓,只要这个人可以代表我们家族就能得到想要的效果。 把在路径上的每个节点都直接连接到根上 ,这就是路径压缩。

int find(int x) {
  //如果x不是祖宗结点
  if (x != p[x]) {
    //让其父节点为祖宗结点
    p[x] = find(p[x]);
  }
  return p[x];
}

合并

宴会上,一个家族的祖先突然对另一个家族说:我们两个家族交情这么好,不如合成一家好了。另一个家族也欣然接受了。
我们之前说过,并不在意祖先究竟是谁,所以只要其中一个祖先变成另一个祖先的儿子就可以了。

//a和b是两个集合的祖宗结点,此时让a的祖宗结点为b
p[a] = b;

posted @ 2021-01-07 16:40  阿-栋  阅读(102)  评论(1编辑  收藏  举报