連通分量
任何連通圖的連通分量只有一個,即是其自身,非連通的無向圖有多個連通分量。
比如:
圖(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);
}