连通分量
任何连通图的连通分量只有一个,即是其自身,非连通的无向图有多个连通分量。
比如:
图(a)的连通分量只有一个,而图(b)的连通分量有6个。
现在有数据如下,怎么判断有多少个连通分量?
int e[10][2] = {
{ 4, 3 }, //两个节点之间相互连接
{ 3, 8 },
{ 6, 5 },
{ 9, 4 },
{ 2, 1 },
{ 8, 9 },
{ 5, 0 },
{ 7, 2 },
{ 6, 1 },
{ 1, 0 },
};
我们主要有两种算法:quick-find算法和quick-union算法
代码实现
ADT
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <assert.h>
#define MAXVEX 100
#define MAXNUM 10
#define BOOLEAN int
#define VTYPE int
#define TRUE 1
#define FALSE 0
typedef struct UF
{
int count; //连通分量数量
VTYPE id[MAXNUM]; //连通分量ID
int(*countUF)(struct UF *);
BOOLEAN(*connectedUF)(struct UF *, VTYPE, VTYPE);
int(*findUF)(struct UF *, VTYPE);
void(*unionUF)(struct UF *, VTYPE, VTYPE);
} UF, *UFPtr;
int countUF(UFPtr this);
BOOLEAN connectedUF(UFPtr this, VTYPE p, VTYPE q);
/*********find算法*********/
int findUF(UFPtr this, VTYPE p);
void unionUF(UFPtr this, VTYPE p, VTYPE q);
/*********find算法*********/
UFPtr newUF();
quick-find算法
quick-find算法通过遍历整个数组,将与p的分量值相同的重命名为q的分量值,使得两个分量归并在一起
int findUF(UFPtr this, VTYPE p) {
return this->id[p];
}
void unionUF(UFPtr this, VTYPE p, VTYPE q) {
//将p和q归并到相同的分量中
int pID = this->findUF(this, p);
int qID = this->findUF(this, q);
if (pID == qID)
return;
//将p的分量重命名为q的名称
for (int i = 0; i < MAXNUM; i++) {
if (this->id[i] == pID) {
this->id[i] = qID;
}
}
this->count--;
}
quick-union算法
quick-union算法是通过构造树,结点的分量指向触点的分量,而根节点的分量则指向自身。union则是将两个分量的根节点都指向其中一个节点的分量,从而实现连通分量归并。
int findUF_2(UFPtr this, VTYPE p) {
//找出根节点的分量名称
while (p != this->id[p])
p = this->id[p];
return p;
}
void unionUF_2(UFPtr this, VTYPE p, VTYPE q) {
int pRoot = this->findUF(this, p);
int qRoot = this->findUF(this, q);
if (pRoot == qRoot)
return;
this->id[pRoot] = qRoot;
this->count--;
}
quick-find和quick-sort的完整代码
#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#include <assert.h>
#define MAXVEX 100
#define MAXNUM 10
#define BOOLEAN int
#define VTYPE int
#define TRUE 1
#define FALSE 0
typedef struct UF
{
int count;
VTYPE id[MAXNUM];
int (*countUF)(struct UF *);
BOOLEAN (*connectedUF)(struct UF *, VTYPE, VTYPE);
int (*findUF)(struct UF *, VTYPE);
void (*unionUF)(struct UF *, VTYPE, VTYPE);
} UF, *UFPtr;
int countUF(UFPtr this) {
return this->count;
}
BOOLEAN connectedUF(UFPtr this, VTYPE p, VTYPE q) {
if (this->findUF(this, p) == this->findUF(this, q))
return TRUE;
return FALSE;
}
/*********quick-find算法*********/
int findUF(UFPtr this, VTYPE p) {
return this->id[p];
}
void unionUF(UFPtr this, VTYPE p, VTYPE q) {
int pID = this->findUF(this, p);
int qID = this->findUF(this, q);
if (pID == qID)
return;
for (int i = 0; i < MAXNUM; i++) {
if (this->id[i] == pID) {
this->id[i] = qID;
}
}
this->count--;
}
/*********quick-find算法*********/
/*********quick-union算法*********/
int findUF_2(UFPtr this, VTYPE p) {
//找出根节点的分量名称
while (p != this->id[p])
p = this->id[p];
return p;
}
void unionUF_2(UFPtr this, VTYPE p, VTYPE q) {
int pRoot = this->findUF(this, p);
int qRoot = this->findUF(this, q);
if (pRoot == qRoot)
return;
this->id[pRoot] = qRoot;
this->count--;
}
/*********quick-union算法*********/
UFPtr newUF() {
UFPtr uf= (UFPtr)malloc(sizeof(UF));
memset(uf, 0, sizeof(uf));
assert(uf != NULL);
uf->count = MAXNUM;
for (int i = 0; i < MAXNUM; i++) {
uf->id[i] = i;
}
uf->countUF = countUF;
uf->connectedUF = connectedUF;
uf->findUF = findUF;
uf->unionUF = unionUF;
return uf;
}
void main() {
// 3,4,8,9
// 6,5,0
// 2,1
int e[10][2] = {
{ 4, 3 },
{ 3, 8 },
{ 6, 5 },
{ 9, 4 },
{ 2, 1 },
{ 8, 9 },
{ 5, 0 },
{ 7, 2 },
{ 6, 1 },
{ 1, 0 },
};
const int e_length_1 = 10;
const int e_length_2 = 2;
UFPtr uf = newUF();
for (int i = 0; i < e_length_1; i++) {
int p = e[i][0]; int q = e[i][1];
if (uf->connectedUF(uf, p, q) == TRUE) {
continue;
}
uf->unionUF(uf, p, q);
}
uf->countUF(uf);
free(uf);
}