并查集

Love The Way You Lie 2022-04-17 02:27 439阅读 0赞

森林:

森林是由若干棵互不相交的树组成,两棵树分别独立,没有交集

20181112082744488.png

并查集:

并查集的结构和森林十分相似,是用于解决不相交集合如下若干几种操作的统称

1.MAKE_SET(x):初始化操作,建立一个只包含元素x的集合

2.UNION(x,y):合并操作,将包含x和y的集合合并成一个新集合

3.FIND_SET(x):查询操作,计算x所在集合

(一般用于求不相交集合的并集)

通常我们用有根树来表示集合,树中的每个结点对应集合中的一个成员

每个成员有一条指向父节点的边,整个有根树通过这些指向父节点的边来维护

每棵树的根就是这个集合的代表,并且这个代表的父节点就是它自己

基本操作:

初始化:

对每个元素都建立一个只包含该元素自己的集合,这意味着每个成员都是自身所在集合的代表,只需将父节点设为它自己

查询:

在不相交的森林中,并查集的查询操作,指的是查出指定元素所在有根树的根节点是谁

合并:

查找到代表元素,将其中一个根节点的父亲设置成另外一个根节点,如下图演示,我们完成了合并操作

20181112084256857.png

集合的传递性(如果a和b同属一个集合,b和c同属一个集合,那么a和c也同属一个集合)

相应算法的实现:

创建一个并查集:

  1. class DDJSSet {
  2. private:
  3. int* father; //用来保存每个结点的父结点
  4. int* rank; //用来保存每个结点的秩
  5. public:
  6. DDJSSet(int size) {
  7. father = new int[size];
  8. rank = new int[size];
  9. for (int i = 0; i < size; ++i) {
  10. father[i] = i; //初始化时,将每个结点的父节点指向它自己
  11. rank[i] = 0;
  12. }
  13. }
  14. ~DDJSSet() {
  15. delete[] father;
  16. delete[] rank;
  17. }
  18. };

查询操作:

1.寻找当前结点的父结点

2.如果父节点是它本身,那么该点就是父节点,如果不是,则返回步骤1继续查找

合并操作:

1.分别获得传入的两个结点所在树的根结点

2.如果两个数所在根节点相同,说明他们在同一棵树上,返回false,结束合并操作

3,如果两个根节点不同,则将一个根节点的父节点指向另一个根节点,返回true结束操作

但是这样做会带来一些问题:我们任选一个结点指向一棵树的父结点,可能会导致一子棵树过长,最终整个并查集退化成一个链表,降低了查找效率,我们可以通过树的高度来判断连接顺序,将高度低的树的根节点连接到高度高的树的根节点

按秩合并:

1.利用一个数组保存每个结点所在树的高度的(初始化为0)

2.获得传入结点的树的根节点

3.若两个根节点不同,比较他们的高度

4.将高度低的树的父节点指向高度高的树的根结点

5.更新合并后的根节点的高度,返回

  1. /*合并操作*/
  2. bool merge(int node1, int node2) {
  3. int ancestor1 = find_set(node1);
  4. int ancestor2 = find_set(node2);
  5. if (ancestor1 != ancestor2) {
  6. if (rank[ancestor1] > rank[ancestor2]) {
  7. swap(ancestor1, ancestor2);
  8. }
  9. father[ancestor1] = ancestor2;
  10. rank[ancestor2] = max(rank[ancestor2], rank[ancestor1] + 1); //更新秩
  11. return true;
  12. }
  13. return false;
  14. }

路径压缩:

20181112092153616.png

如上图所示,如果我们把每一个子节点都指向这个并查集的根节点,这样会大大地降低查找效率

在执行寻找根节点的函数中,我们稍作修改,将查找路径上的所有结点都指向最终查找到的根节点

  1. /*路径压缩算法*/
  2. int find_set(int node) {
  3. if (father[node] != node) {
  4. father[node] = find_set(father[node]);
  5. }
  6. return father[node];
  7. }

整体代码:

  1. #include <iostream>
  2. #include <algorithm>
  3. using namespace std;
  4. class zDisjointSet {
  5. private:
  6. int* father;
  7. int* rank;
  8. public:
  9. zDisjointSet(int size) {
  10. father = new int[size];
  11. rank = new int[size];
  12. for (int i = 0; i < size; ++i) {
  13. father[i] = i;
  14. rank[i] = 0;
  15. }
  16. }
  17. ~zDisjointSet() {
  18. delete[] father;
  19. delete[] rank;
  20. }
  21. /*路径压缩算法*/
  22. int find_set(int node) {
  23. if (father[node] != node) {
  24. father[node] = find_set(father[node]);
  25. }
  26. return father[node];
  27. }
  28. /*合并操作*/
  29. bool merge(int node1, int node2) {
  30. int ancestor1 = find_set(node1);
  31. int ancestor2 = find_set(node2);
  32. if (ancestor1 != ancestor2) {
  33. if (rank[ancestor1] > rank[ancestor2]) {
  34. swap(ancestor1, ancestor2);
  35. }
  36. father[ancestor1] = ancestor2;
  37. rank[ancestor2] = max(rank[ancestor2], rank[ancestor1] + 1); //更新秩
  38. return true;
  39. }
  40. return false;
  41. }
  42. };

#

发表评论

表情:
评论列表 (有 0 条评论,439人围观)

还没有评论,来说两句吧...

相关阅读

    相关

    并查集JAVA版框架 并查集是一种用来管理元素分组情况的数据结构。 并查集可以高效地进行如下操作: \--查询元素a和元素b是否属于同一组

    相关

    这个文章是几年前水acm的时候转的, 当时也不知道作者是谁, 要是有人知道的话说一下吧 并查集是我暑假从高手那里学到的一招,觉得真是太精妙的设计了。以前我无法解决的

    相关

    > 题目 > 某学校近期要组织全校同学出去参加某项活动,由于人数众多,学校决定让同学们自行组队,以小组为单位进行活动。假设学校一共n个同学,每个同学有一个唯一的数字作为标签

    相关

    森林: 森林是由若干棵互不相交的树组成,两棵树分别独立,没有交集 ![20181112082744488.png][] 并查集: 并查集的结构和森林十分相似,是

    相关

    来看一个实例,[杭电1232畅通工程][1232] 首先在地图上给你若干个城镇,这些城镇都可以看作点,然后告诉你哪些对城镇之间是有道路直接相连的。最后要解决的是整幅图的连通性

    相关

    > 并查集是一种树形的数据结构,用于集合的合并。 与C++STL相比的优势: 1. 可以同时维护多个集合 2. 快速的合并两个集合 \\现实: 用数组实现。fat

    相关

    例题: ![输入样例][20190805122001677.png] 输入样例,第一行为一个整数n,代表有n台电脑,编号1-n;接下来进行一系列操作,当输入‘c’时,判

    相关

    一、算法解释 用于解决一些有N个元素的集合应用问题。 1、将每个元素初始化为自身单独成为一个集合。用p\[i\]的值表示该元素所在集合。 ![这里写图片描述][S

    相关

    并查集的作用就是快速判断两个元素是否在同一个集合中,快速将两个集合合并 基本模板 include <iostream> include <a