不相交集合

来源:互联网 发布:网络隔离设备 编辑:程序博客网 时间:2024/05/16 11:13

不相交集合(disjoint-set

1、不相交集合数据结构

不相交集合数据结构(disjoint-set data structure)维护着一组不相交的动态集合S = {S1, S2, S3, ... , Sk}。我们有一个代表(representative来标识(identify每个集合,它是这个集合中的某个成员。

x表示一个对象,支持一下操作:

 

2、不相交集合的链表表示

 

使用加权合并启发式策略(weighted-union heuristic

//==============================================================// 不相交集合的链表实现//==============================================================#include <stdio.h>#include <stdlib.h>struct set;typedef struct snode {int elem;struct snode *next;struct set *head;} SNode;typedef struct set {int size; // 记录集合的大小struct snode *head;struct snode *tail;} Set;//==============================================================// 建立新的集合,其包含唯一的元素elem//==============================================================Set *MakeSet(int elem){SNode *p = (SNode *)malloc(sizeof(SNode));if (!p) {printf("p malloc error\n");return NULL;}p->elem = elem;p->next = NULL;Set *s = (Set *)malloc(sizeof(Set));if (!s) {free(p);printf("s malloc error\n");return NULL;}s->head = s->tail = p;s->size = 1;p->head = s;return s;}//==============================================================// 将s1、s2合并为一个新的集合//==============================================================Set *UnionSet(Set *s1, Set *s2){if (s1 == NULL)return s2;if (s2 == NULL)return s1;// 使用加权合并启发式策略if (s1->size > s2->size) { // 以s1为新集合的代表SNode **ptail = &s1->tail; // 与else中实现的效果相同(*ptail)->next = s2->head; // 将s2中元素加入s1中while ((*ptail)->next) {   // 更新s1中元素的代表*ptail = (*ptail)->next;(*ptail)->head = s1; }s1->size += s2->size; // 更新集合大小return s1;} else {SNode *tail = s2->tail;tail->next = s1->head;while (tail->next) {tail = tail->next;tail->head = s1; // 更新s2的代表}s2->tail = tail;s2->size += s1->size;return s2;}// SNode *tail = NULL;// Set *s = NULL;// if (s1->size > s2->size) { // 以s1为新集合的代表// tail = s1->tail;// tail->next = s2->head;// s = s1;// s->size += s2->size;// } else {// tail = s2->tail;// tail->next = s1->head;// s = s2;// s->size += s1->size;// }// // while (tail->next) {// tail = tail->next;// tail->head = s; // 更新s1的代表// }// s->tail = tail;// // return s;}//==============================================================// 获得包含x集合的代表//==============================================================SNode *FindSet(SNode *x){return x->head->head;}//==============================================================// 打印集合s中的元素//==============================================================void PrintSet(Set *s){SNode *p = s->head;while (p) {printf("%d\t", p->elem);p = p->next;}printf("\n");}int main(){int arr[] = {2, 24, 34, 12, 3, 67, 876, 3, 23, 9};int len = sizeof(arr) / sizeof(arr[0]);Set *set[BUFSIZE];for (int i = 0; i < len; i++) {set[i] = MakeSet(arr[i]);}Set *s = NULL;Set *s1 = NULL;Set *s2 = NULL;for (int i = 0; i < 4; i++) {s1 = UnionSet(s1, set[i]);PrintSet(s1);}for (int i = 4; i < len; i++)s2 = UnionSet(s2, set[i]);PrintSet(s2);s = UnionSet(s1, s2);PrintSet(s);system("pause");return 0;}

3、不相交集合森林

我们通过有根树来表示集合,其中每个结点包含一个成员,每棵树代表一个集合。在不相交集合森林中,每个成员仅指向其父结点。每棵树的根包含代表,并且是其自己的父节点。

 

我们可以通过两种启发式策略来提高运行时间:

1)按秩合并(union by rank:对于每个结点,我们维护一个秩(rank),即该结点高度的一个上界。在UnionSet操作中,我们让秩小的根指向秩大的根;

2路径压缩(path compression:在FindSet操作中,我们让查找路径上的每个结点直接指向根结点。路径压缩不改变任何秩。

 

//=============================================================// 不相交集合森林//=============================================================#include <stdio.h>#include <stdlib.h>#define BUFSIZE 100#define VERTEXS 10int rank[BUFSIZE];int parent[BUFSIZE];int Graph[VERTEXS][VERTEXS] = { // a, b, c, d, e, f, g, h, i, j    0, 1, 1, 0, 0, 0, 0, 0, 0, 0, // a    1, 0, 1, 1, 0, 0, 0, 0, 0, 0, // b    1, 1, 0, 0, 0, 0, 0, 0, 0, 0, // c    0, 1, 0, 0, 0, 0, 0, 0, 0, 0, // d    0, 0, 0, 0, 0, 1, 1, 0, 0, 0, // e    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // f    0, 0, 0, 0, 1, 0, 0, 0, 0, 0, // g    0, 0, 0, 0, 0, 0, 0, 0, 1, 0, // h    0, 0, 0, 0, 0, 0, 0, 1, 0, 0, // i    0, 0, 0, 0, 0, 0, 0, 0, 0, 0  // j};//============================================================// 建立新的集合,初始化parent、rank// ===========================================================void MakeSet(int x){parent[x] = x;rank[x] = 0;}//===========================================================// 带路径压缩(path compression)的FindSet//===========================================================int FindSet(int x){if (x != parent[x])parent[x] = FindSet(parent[x]); return parent[x];// return (x == parent[x] ? x : FindSet[parent[x]]);}//===========================================================// 链接x、y,必要时更行rank(rank[x] == rank[y])//===========================================================void Link(int x, int y){if (rank[x] > rank[y]) {parent[y] = x;} else {parent[x] = y;if (rank[x] == rank[y]) rank[y]++;}}//===========================================================// 合并x、y//===========================================================void UnionSet(int x, int y){Link(FindSet(x), FindSet(y));}//===========================================================// 判断x、y是否在同一个分量中//===========================================================bool SameComponent(int x, int y){return FindSet(x) == FindSet(y);}//===========================================================// 获取Graph的连通分量//===========================================================void ConnectedComponents(){for (int i = 0; i < VERTEXS; i++) {MakeSet(i);}for (int u = 0; u < VERTEXS; u++) {for (int v = 0; v < VERTEXS; v++) {if (Graph[u][v] != 0 && !SameComponent(u, v))UnionSet(u, v);}}}int main(){ConnectedComponents();printf("parent:");for (int i = 0; i < VERTEXS; i++) {printf("%d\t", parent[i]);}printf("\n");printf("rank:");for (int i = 0; i < VERTEXS; i++) {printf("%d\t", rank[i]);}printf("\n");system("pause");return 0;}


0 0