最小生成树(MST):给定一加权无向图,找出它的一颗最小生成树。
定义:图的最小生成树是它的一副含有其所有顶点的无环连通子图。一副加权图的最小生成树是它的一颗权值(树种所有边的权值之和)最小的生成树。
我们约定:
1. 只考虑连通图
2. 边的权重不一定代表距离
3. 边的权重可能是0或者负数
4. 所有边的权重都各不相同。如果不同边的权重可以相同,最小生成树就不一定唯一了。存在多颗最小生成树的可能性会使部分算法的证明变得更加复杂。
命题J(切分定理):在一副加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图中的最小生成树。
切分定理是解决最小生成树问题的所有算法的基础。更确切的说,这些算法都是贪心算法的特殊情况:使用切分定理找到最小生成树的一条边,不断重复直到找到最小生成树的所有边。
命题K(最小生成树的贪心算法):将含有V个顶点的任意加权连通图中属于最小生成树的边标记为黑色:初始状态下所有边均为灰色,找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了V-1条黑色边为止。
怎么理解切分定理呢?
Prim算法其实就是切分定理的应用,我们可以通过分析Prime算法,来理解切分定理。
我从链接数据结构(七)图了解了实现Prim算法的实现过程。
1)选择v3为起点,与v3连接的边中,权值最小的边为5,连接的点位v1, 框选除v1和v3作为一个整体
2)在v1,v3的整体中,与之连接的边中,权值最小的边为4,连接的点位v2,框选v1,v2,v3作为一个整体
3)重复以上步骤,直到遍历了所有的节点。
切分定理:在一副加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图中的最小生成树。
为什么横切边权重最小者必然属于图的最小生成树呢?我们由简入繁。
1. 图没有环,即图是一棵树
2. 图是一个环
3. 图是环和树的简单连接
所以对于更复杂的情况,无非是更多的环和更多的树的组合。
通过切分定理,我们就可以理解Prim算法了。
基本思路是将所有顶点放入一张新的图,然后再进行Prim算法,寻找节点的最小权重edge,再将数据放入新的图中。
ADT:
#include
#include
#include
#define MAXVEX 100
#define VTYPE char
typedef struct EdgeNode {
VTYPE adjvex;
int weight;
struct EdgeNode *next;
} Edge, *EdgePtr;
typedef struct VertexNode {
VTYPE data;
EdgePtr firstEdge;
} Vertex, *VertexPtr;
typedef struct {
VertexPtr vertexList[MAXVEX];
int vNum;
} AdjGraph, *AdjGraphPtr;
void freeAdj(AdjGraphPtr a);
EdgePtr createEdgeNode(VTYPE key, int weight);
int getVertexPos(const AdjGraphPtr m, const VTYPE key);
void insertVertex(AdjGraphPtr adj, const VTYPE key);
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2);
void print(int *a, int length);
void minEdge(AdjGraphPtr adj, AdjGraphPtr primTree, int *marked);
int isMarkedFinish(int *marked, int length) ;
void lazyPrimMST(AdjGraphPtr adj, AdjGraphPtr primTree, int *marked);
void primMST(AdjGraphPtr adj);
完整代码:
#include
#include
#include
#define MAXVEX 100
#define VTYPE char
typedef struct EdgeNode {
VTYPE adjvex;
int weight;
struct EdgeNode *next;
} Edge, *EdgePtr;
typedef struct VertexNode {
VTYPE data;
EdgePtr firstEdge;
} Vertex, *VertexPtr;
typedef struct {
VertexPtr vertexList[MAXVEX];
int vNum;
} AdjGraph, *AdjGraphPtr;
void freeAdj(AdjGraphPtr a);
EdgePtr createEdgeNode(VTYPE key, int weight);
int getVertexPos(const AdjGraphPtr m, const VTYPE key);
void insertVertex(AdjGraphPtr adj, const VTYPE key);
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2);
void print(int *a, int length);
void minEdge(AdjGraphPtr adj, AdjGraphPtr primTree, int *marked) {
*marked = 1;
AdjGraphPtr adjTmp = (AdjGraphPtr)malloc(sizeof(AdjGraph)); //将所有待验证的边放入一张图中
memset(adjTmp, 0, sizeof(AdjGraph));
//插入已经访问过的节点
for (int i = 0; i < adj->vNum; i++) {
if (*(marked + i) == 1) { //顶点
VTYPE vertexData = adj->vertexList[i]->data;
insertVertex(adjTmp, vertexData);
}
}
//找出所有的节点所对应的权重边
for (int i = 0; i < adjTmp->vNum; i++) {
int vPos = getVertexPos(adj, adjTmp->vertexList[i]->data);
EdgePtr tmp = adj->vertexList[vPos]->firstEdge;
VTYPE vertexData = adj->vertexList[vPos]->data;
while (tmp != NULL) { //edge
int pos = getVertexPos(adj, tmp->adjvex);
if (*(marked + pos) != 1) { //未被标记 //顶点被标记,edge未被标记
VTYPE edgeData = tmp->adjvex;
int weight = tmp->weight;
insertEdge(adjTmp, vertexData, edgeData, weight);
}
tmp = tmp->next;
}
free(tmp);
}
//找出节点的最小权重边
VTYPE minVertexData = adjTmp->vertexList[0]->data;
VTYPE minEdgeData = adjTmp->vertexList[0]->firstEdge->adjvex;;
int minWeight = adjTmp->vertexList[0]->firstEdge->weight;
for (int i = 0; i < adjTmp->vNum; i++) { //遍历所有顶点
EdgePtr tmp= adjTmp->vertexList[i]->firstEdge;
while (tmp != NULL) {
if (minWeight > tmp->weight) {
minEdgeData = tmp->adjvex;
minWeight = tmp->weight;
minVertexData = adjTmp->vertexList[i]->data;
}
tmp = tmp->next;
}
}
//将最小权重边插入到新图中
int minEdgePos = getVertexPos(adj, minEdgeData);
*(marked + minEdgePos) = 1;
free(adjTmp);
insertEdge(primTree, minVertexData, minEdgeData, minWeight);
insertEdge(primTree, minEdgeData, minVertexData, minWeight);
}
int isMarkedFinish(int *marked, int length) {
print(marked, length);
for(int i=0; iif (*(marked + i) == 0)
return 0;
}
return 1;
}
void lazyPrimMST(AdjGraphPtr adj, AdjGraphPtr primTree, int *marked) {
//从顶点开始遍历
while (isMarkedFinish(marked, adj->vNum) == 0)
{
minEdge(adj, primTree, marked);
}
}
void primMST(AdjGraphPtr adj) {
int *marked = (int *)malloc(adj->vNum * sizeof(int));
for (int i = 0; i < adj->vNum; i++) {
*(marked + i) = 0;
}
AdjGraphPtr primTree = (AdjGraphPtr)malloc(sizeof(AdjGraph));
memset(primTree, 0, sizeof(AdjGraph));
for (int i = 0; i < adj->vNum; i++) {
insertVertex(primTree, adj->vertexList[i]->data);
}
lazyPrimMST(adj, primTree, marked);
free(marked);
freeAdj(primTree);
}
void main() {
int e[7][3] = {
{ 'A', 'F', 60 },
{ 'A', 'E', 30 },
{ 'A', 'C', 10 },
{ 'F', 'E', 20 },
{ 'E', 'D', 50 },
{ 'D', 'C', 11 },
{ 'C', 'B', 5 },
};
const int e_length_1 = 7;
const int e_length_2 = 3;
VTYPE v[6] = { 'A','F','E','D','C','B' };
const int v_length = 6;
AdjGraphPtr adj = createAdjGraph(v, v_length, e, e_length_1, e_length_2);
primMST(adj);
freeAdj(adj);
}
void freeAdj(AdjGraphPtr a) {
for (int i = 0; i < a->vNum; i++) {
if (a->vertexList[i]->firstEdge != NULL) {
EdgePtr tmp = a->vertexList[i]->firstEdge;
while (tmp->next != NULL) {
EdgePtr t = tmp;
tmp = tmp->next;
free(t);
}
free(tmp);
}
free(a->vertexList[i]);
}
free(a);
}
EdgePtr createEdgeNode(VTYPE key, int weight) {
EdgePtr a = (EdgePtr)malloc(sizeof(Edge));
memset(a, 0, sizeof(Edge));
a->adjvex = key;
a->weight = weight;
a->next = NULL;
return a;
}
int getVertexPos(const AdjGraphPtr m, const VTYPE key) {
for (int i = 0; i < m->vNum; i++) {
if (m->vertexList[i]->data == key)
return i;
}
return -1;
}
void insertVertex(AdjGraphPtr adj, const VTYPE key) {
for (int i = 0; i < adj->vNum; i++) {
if (adj->vertexList[i] == key)
return;
}
VertexPtr vNode = (VertexPtr)malloc(sizeof(Vertex));
memset(vNode, 0, sizeof(Vertex));
vNode->data = key;
vNode->firstEdge = NULL;
adj->vertexList[adj->vNum] = vNode;
adj->vNum++;
}
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight) {
int pos_a = getVertexPos(adj, a);
EdgePtr avex = createEdgeNode(b, weight);
if (adj->vertexList[pos_a]->firstEdge == NULL) {
adj->vertexList[pos_a]->firstEdge = avex;
}
else {
EdgePtr tmp = adj->vertexList[pos_a]->firstEdge;
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = avex;
}
}
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2) {
AdjGraphPtr adj = (AdjGraphPtr)malloc(sizeof(AdjGraph));
memset(adj, 0, sizeof(AdjGraph));
for (int i = 0; i < v_length; i++) {
insertVertex(adj, v[i]);
}
for (int i = 0; i < e_length_1; i++) {
VTYPE a = *(e + e_length_2 * i + 0);
VTYPE b = *(e + e_length_2 * i + 1);
int weight = *(e + e_length_2 * i + 2);
insertEdge(adj, a, b, weight);
insertEdge(adj, b, a, weight);
}
return adj;
}
void print(int *a, int length) {
for (int i = 0; i < length; i++) {
printf("%d ", a[i]);
}
putchar('\n');
}
代码 1 的实现比较复杂,另一种比较直观的方法则用到了数据结构(注:参考自《算法》4.3.4.1):
我们可以重建图的结构,向图的边添加顶点, 相应的函数做出一些调整。
图ADT:
#define MAXVEX 100
#define VTYPE char
typedef struct EdgeNode {
VTYPE v; //change
VTYPE w;
int weight;
struct EdgeNode *next;
} Edge, *EdgePtr;
typedef struct VertexNode {
VTYPE v;
EdgePtr firstEdge;
} Vertex, *VertexPtr;
typedef struct {
VertexPtr vertexList[MAXVEX];
int vNum;
int eNum; //change
} AdjGraph, *AdjGraphPtr;
void freeAdj(AdjGraphPtr a);
EdgePtr createEdgeNode(VTYPE key, int weight);
int getVertexPos(const AdjGraphPtr m, const VTYPE key);
void insertVertex(AdjGraphPtr adj, const VTYPE key);
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
void addEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2);
void print(int *a, int length);
队列ADT:
#define MAXVEX 100
#define VTYPE char
#define QUETYPE EdgePtr
#define SORTTYPE int
#define TRUE 1
#define FALSE 0
typedef struct node {
QUETYPE data;
struct node *next;
} QueueNode, *QueueNodePtr;
typedef struct {
QueueNodePtr head;
QueueNodePtr tail;
int size;
} Queue, *QueuePtr;
QueuePtr createQueue();
QueueNodePtr createQueueNode(QUETYPE key);
void insertQueue(QueuePtr q, QUETYPE key, int isSort);
QUETYPE outQueue(QueuePtr q);
void deleteQueue(QueuePtr q);
/**********队列排序**********/
//需要自己重写队列的排序算法
SORTTYPE sortData(QueueNodePtr q);
void exchange(QueueNodePtr a, QueueNodePtr b);
void queueSort(QueuePtr q);
/**********队列排序**********/
完整代码:
#include
#include
#include
#define MAXVEX 100
#define VTYPE char
#define QUETYPE EdgePtr
#define SORTTYPE int
#define TRUE 1
#define FALSE 0
typedef struct EdgeNode {
VTYPE v;
VTYPE w;
int weight;
struct EdgeNode *next;
} Edge, *EdgePtr;
typedef struct VertexNode {
VTYPE v;
EdgePtr firstEdge;
} Vertex, *VertexPtr;
typedef struct {
VertexPtr vertexList[MAXVEX];
int vNum;
int eNum;
} AdjGraph, *AdjGraphPtr;
void freeAdj(AdjGraphPtr a);
EdgePtr createEdgeNode(VTYPE key, int weight);
int getVertexPos(const AdjGraphPtr m, const VTYPE key);
void insertVertex(AdjGraphPtr adj, const VTYPE key);
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
void addEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2);
void print(int *a, int length);
typedef struct node {
QUETYPE data;
struct node *next;
} QueueNode, *QueueNodePtr;
typedef struct {
QueueNodePtr head;
QueueNodePtr tail;
} Queue, *QueuePtr;
QueuePtr createQueue();
QueueNodePtr createQueueNode(QUETYPE key);
void insertQueue(QueuePtr q, QUETYPE key, int isSort);
QUETYPE outQueue(QueuePtr q);
void deleteQueue(QueuePtr q);
/**********队列排序**********/
SORTTYPE sortData(QueueNodePtr q) {
return q->data->weight;
}
void exchange(QueueNodePtr a, QueueNodePtr b) {
SORTTYPE *a1 = &(a->data->weight);
SORTTYPE tmp_a = a->data->weight;
SORTTYPE *b1 = &(b->data->weight);
*a1 = b->data->weight;
*b1 = tmp_a;
VTYPE *a2 = &(a->data->v);
VTYPE tmp_a2 = a->data->v;
VTYPE *b2 = &(b->data->v);
*a2 = b->data->v;
*b2 = tmp_a2;
VTYPE *a3 = &(a->data->w);
VTYPE tmp_a3 = a->data->w;
VTYPE *b3 = &(b->data->w);
*a3 = b->data->w;
*b3 = tmp_a3;
}
void queueSort(QueuePtr q) {
//选择排序
QueueNodePtr tmp = NULL;
tmp = q->head;
while (tmp != NULL) {
QueueNodePtr tmp2 = tmp;
SORTTYPE data = sortData(tmp);
while (tmp2 != NULL) {
if (sortData(tmp2) < data) {
exchange(tmp, tmp2);
}
tmp2 = tmp2->next;
}
tmp = tmp->next;
}
}
/**********队列排序**********/
int isEdgeInQueue(EdgePtr tmp, QueuePtr q) {
QueueNodePtr tmpQue = q->head;
while (tmpQue != NULL) {
if (tmpQue->data->weight == tmp->weight) {
if (tmpQue->data->v == tmp->v && tmpQue->data->w == tmp->w) {
return TRUE;
}
if (tmpQue->data->v == tmp->w && tmpQue->data->w == tmp->v) {
return TRUE;
}
}
tmpQue = tmpQue->next;
}
return FALSE;
}
void visit(AdjGraphPtr adj, int *marked, int pos, QueuePtr minPQ) {
// 标记顶点,并将所有连接v和未被标记顶点的边加入minPQ
marked[pos] = 1;
EdgePtr tmp = adj->vertexList[pos]->firstEdge;
while (tmp !=NULL)
{
insertQueue(minPQ, tmp, 1);
tmp = tmp->next;
}
}
void LazyPrimMST(AdjGraphPtr adj) {
QueuePtr minPQ = createQueue();
QueuePtr mst = createQueue();
int marked[6]; //注意修改长度
for (int i = 0; i < 6; i++) {
marked[i] = 0;
}
visit(adj, marked, 0, minPQ); //选择一个点添加到树中
while (isQueueEmpty(minPQ) != TRUE) {
EdgePtr e = outQueue(minPQ);
VTYPE v = e->v; VTYPE w = e->w;
int v_pos = getVertexPos(adj, v);
int w_pos = getVertexPos(adj, w);
if (marked[v_pos] == 1 && marked[w_pos] == 1) //跳过失效的边
continue;
EdgePtr tmp = createEdgeNode(v, w, e->weight);
if (isEdgeInQueue(tmp, mst) == FALSE)
insertQueue(mst, tmp, 0);
if (!marked[v_pos]) //将边添加到树中
visit(adj, marked, v_pos, minPQ);
if (!marked[w_pos]) //将顶点添加到树中
visit(adj, marked, w_pos, minPQ);
}
deleteQueue(mst);
deleteQueue(minPQ);
}
void main() {
int e[7][3] = {
{ 'A', 'F', 60 },
{ 'A', 'E', 30 },
{ 'A', 'C', 10 },
{ 'F', 'E', 20 },
{ 'E', 'D', 50 },
{ 'D', 'C', 11 },
{ 'C', 'B', 5 },
};
const int e_length_1 = 7;
const int e_length_2 = 3;
VTYPE v[6] = { 'A','F','E','D','C','B' };
const int v_length = 6;
AdjGraphPtr adj = createAdjGraph(v, v_length, e, e_length_1, e_length_2);
LazyPrimMST(adj);
freeAdj(adj);
}
void freeAdj(AdjGraphPtr a) {
for (int i = 0; i < a->vNum; i++) {
if (a->vertexList[i]->firstEdge != NULL) {
EdgePtr tmp = a->vertexList[i]->firstEdge;
while (tmp->next != NULL) {
EdgePtr t = tmp;
tmp = tmp->next;
free(t);
}
free(tmp);
}
free(a->vertexList[i]);
}
free(a);
}
EdgePtr createEdgeNode(VTYPE v, VTYPE w, int weight) {
EdgePtr a = (EdgePtr)malloc(sizeof(Edge));
memset(a, 0, sizeof(Edge));
a->v = v;
a->w = w;
a->weight = weight;
a->next = NULL;
return a;
}
int getVertexPos(const AdjGraphPtr m, const VTYPE key) {
for (int i = 0; i < m->vNum; i++) {
if (m->vertexList[i]->v == key)
return i;
}
return -1;
}
void insertVertex(AdjGraphPtr adj, const VTYPE key) {
for (int i = 0; i < adj->vNum; i++) {
if (adj->vertexList[i] == key)
return;
}
VertexPtr vNode = (VertexPtr)malloc(sizeof(Vertex));
memset(vNode, 0, sizeof(Vertex));
vNode->v = key;
vNode->firstEdge = NULL;
adj->vertexList[adj->vNum] = vNode;
adj->vNum++;
}
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight) {
int pos_a = getVertexPos(adj, a);
EdgePtr avex = createEdgeNode(a, b, weight);
if (adj->vertexList[pos_a]->firstEdge == NULL) {
adj->vertexList[pos_a]->firstEdge = avex;
}
else {
EdgePtr tmp = adj->vertexList[pos_a]->firstEdge;
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = avex;
}
}
void addEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight) {
insertEdge(adj, a, b, weight);
insertEdge(adj, b, a, weight);
adj->eNum++;
}
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2) {
AdjGraphPtr adj = (AdjGraphPtr)malloc(sizeof(AdjGraph));
memset(adj, 0, sizeof(AdjGraph));
for (int i = 0; i < v_length; i++) {
insertVertex(adj, v[i]);
}
for (int i = 0; i < e_length_1; i++) {
VTYPE a = *(e + e_length_2 * i + 0);
VTYPE b = *(e + e_length_2 * i + 1);
int weight = *(e + e_length_2 * i + 2);
addEdge(adj, a, b, weight);
}
return adj;
}
void print(int *a, int length) {
for (int i = 0; i < length; i++) {
printf("%d ", a[i]);
}
putchar('\n');
}
QueuePtr createQueue() {
QueuePtr q = (QueuePtr)malloc(sizeof(Queue));
memset(q, 0, sizeof(Queue));
q->size = 0;
q->head = q->tail = NULL;
return q;
}
QueueNodePtr createQueueNode(QUETYPE key) {
QueueNodePtr q = (QueueNodePtr)malloc(sizeof(Queue));
memset(q, 0, sizeof(Queue));
q->next = NULL;
q->data = key;
return q;
}
void insertQueue(QueuePtr q, QUETYPE key, int isSort) {
QueueNodePtr a = createQueueNode(key);
if (q->head == NULL) { //notice
q->head = q->tail = a;
}
else {
q->tail->next = a;
q->tail = a;
}
q->size++;
if(isSort == 1) //自动排序
queueSort(q);
}
QUETYPE outQueue(QueuePtr q) {
QUETYPE v;
if (q->head != NULL) { //notice
v = q->head->data;
QueueNodePtr tmp = q->head->next;
free(q->head);
q->head = tmp;
}
else {
v = NULL;
}
return v;
}
void deleteQueue(QueuePtr q) {
if (q->head != NULL) { //notice
QueueNodePtr tmp = q->head;
QueueNodePtr tmp2;
while (tmp != NULL) {
tmp2 = tmp;
tmp = tmp->next;
free(tmp2);
}
}
free(q);
}
int isQueueEmpty(QueuePtr q) {
if (q->head == q->tail) {
return TRUE;
}
return FALSE;
}
命题K(最小生成树的贪心算法):将含有V个顶点的任意加权连通图中属于最小生成树的边标记为黑色:初始状态下所有边均为灰色,找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了V-1条黑色边为止。
讨论最简单的情况,最小权重边刚好成环,如下:
1. 找出图的最小权重边1,标记v1,v3
2. 找出图除1外的最小权重边3,标记v1, v2
3. 找出图除1,3外的最小权重边4,很显然我们形成了一个环,这个时候我们有一个不必要的,所以舍弃边4
4. 重复找出最小权重边,直到遍历完成
相比prim算法代码实现2,kruskal算法用到的数据结构如下:
* 队列,用于保存mst(同代码实现2)
* 优先队列,用于保存所有横切边(同代码实现2)
* union-find,用于保存所有分量
核心代码:
void kruskalMST(AdjGraphPtr adj) {
QueuePtr mst = createQueue();
QueuePtr minPQ = createQueue();
insertAllEdgeToQueue(adj, minPQ); //将所有横切边放入到一个队列
UFPtr uf = newUF();
uf->count = adj->vNum;
while (isQueueEmpty(minPQ) == FALSE && mst->size < adj->vNum) {
EdgePtr e = outQueue(minPQ); //权重最小的边
VTYPE v = e->v; VTYPE w = e->w;
int v_pos = getVertexPos(adj, v);
int w_pos = getVertexPos(adj, w);
if (uf->connectedUF(uf, v_pos, w_pos) == TRUE) { //忽略失效的边
continue;
}
else {
uf->unionUF(uf, v_pos, w_pos); //合并分量
insertQueue(mst, e, 0); //将边添加到最小生成树
}
}
free(mst);
free(minPQ);
free(uf);
}
完整代码:
#include
#include
#include
#include
#define MAXVEX 100
#define MAXNUM 10
#define VTYPE char
#define QUETYPE EdgePtr
#define SORTTYPE int
#define BOOLEAN 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();
typedef struct EdgeNode {
VTYPE v;
VTYPE w;
int weight;
struct EdgeNode *next;
} Edge, *EdgePtr;
typedef struct VertexNode {
VTYPE v;
EdgePtr firstEdge;
} Vertex, *VertexPtr;
typedef struct {
VertexPtr vertexList[MAXVEX];
int vNum;
int eNum;
} AdjGraph, *AdjGraphPtr;
void freeAdj(AdjGraphPtr a);
EdgePtr createEdgeNode(VTYPE key, int weight);
int getVertexPos(const AdjGraphPtr m, const VTYPE key);
void insertVertex(AdjGraphPtr adj, const VTYPE key);
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
void addEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight);
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2);
void print(int *a, int length);
typedef struct node {
QUETYPE data;
struct node *next;
} QueueNode, *QueueNodePtr;
typedef struct {
QueueNodePtr head;
QueueNodePtr tail;
int size;
} Queue, *QueuePtr;
QueuePtr createQueue();
QueueNodePtr createQueueNode(QUETYPE key);
void insertQueue(QueuePtr q, QUETYPE key, int isSort);
QUETYPE outQueue(QueuePtr q);
void deleteQueue(QueuePtr q);
/**********队列排序**********/
SORTTYPE sortData(QueueNodePtr q) {
return q->data->weight;
}
void exchange(QueueNodePtr a, QueueNodePtr b) {
SORTTYPE *a1 = &(a->data->weight);
SORTTYPE tmp_a = a->data->weight;
SORTTYPE *b1 = &(b->data->weight);
*a1 = b->data->weight;
*b1 = tmp_a;
VTYPE *a2 = &(a->data->v);
VTYPE tmp_a2 = a->data->v;
VTYPE *b2 = &(b->data->v);
*a2 = b->data->v;
*b2 = tmp_a2;
VTYPE *a3 = &(a->data->w);
VTYPE tmp_a3 = a->data->w;
VTYPE *b3 = &(b->data->w);
*a3 = b->data->w;
*b3 = tmp_a3;
}
void queueSort(QueuePtr q) {
//选择排序
QueueNodePtr tmp = NULL;
tmp = q->head;
while (tmp != NULL) {
QueueNodePtr tmp2 = tmp;
SORTTYPE data = sortData(tmp);
while (tmp2 != NULL) {
if (sortData(tmp2) < data) {
exchange(tmp, tmp2);
}
tmp2 = tmp2->next;
}
tmp = tmp->next;
}
}
/**********队列排序**********/
int isEdgeInQueue(EdgePtr tmp, QueuePtr q) {
QueueNodePtr tmpQue = q->head;
while (tmpQue != NULL) {
if (tmpQue->data->weight == tmp->weight) {
if (tmpQue->data->v == tmp->v && tmpQue->data->w == tmp->w) {
return TRUE;
}
if (tmpQue->data->v == tmp->w && tmpQue->data->w == tmp->v) {
return TRUE;
}
}
tmpQue = tmpQue->next;
}
return FALSE;
}
void insertAllEdgeToQueue(AdjGraphPtr adj, QueuePtr q) {
for (int i = 0; i < adj->vNum; i++) {
EdgePtr tmp = adj->vertexList[i]->firstEdge;
while (tmp != NULL) {
if (isEdgeInQueue(tmp, q) == 0) //tmp不在队列中
insertQueue(q, tmp, 1);
tmp = tmp->next;
}
}
}
void kruskalMST(AdjGraphPtr adj) {
QueuePtr mst = createQueue();
QueuePtr minPQ = createQueue();
insertAllEdgeToQueue(adj, minPQ); //将所有横切边放入到一个队列
UFPtr uf = newUF();
uf->count = adj->vNum;
while (isQueueEmpty(minPQ) == FALSE && mst->size < adj->vNum) {
EdgePtr e = outQueue(minPQ); //权重最小的边
VTYPE v = e->v; VTYPE w = e->w;
int v_pos = getVertexPos(adj, v);
int w_pos = getVertexPos(adj, w);
if (uf->connectedUF(uf, v_pos, w_pos) == TRUE) { //忽略失效的边
continue;
}
else {
uf->unionUF(uf, v_pos, w_pos); //合并分量
insertQueue(mst, e, 0); //将边添加到最小生成树
}
}
free(mst);
free(minPQ);
free(uf);
}
void main() {
int e[7][3] = {
{ 'A', 'F', 60 },
{ 'A', 'E', 30 },
{ 'A', 'C', 10 },
{ 'F', 'E', 20 },
{ 'E', 'D', 50 },
{ 'D', 'C', 11 },
{ 'C', 'B', 5 },
};
const int e_length_1 = 7;
const int e_length_2 = 3;
VTYPE v[6] = { 'A','F','E','D','C','B' };
const int v_length = 6;
AdjGraphPtr adj = createAdjGraph(v, v_length, e, e_length_1, e_length_2);
kruskalMST(adj);
freeAdj(adj);
}
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 freeAdj(AdjGraphPtr a) {
for (int i = 0; i < a->vNum; i++) {
if (a->vertexList[i]->firstEdge != NULL) {
EdgePtr tmp = a->vertexList[i]->firstEdge;
while (tmp->next != NULL) {
EdgePtr t = tmp;
tmp = tmp->next;
free(t);
}
free(tmp);
}
free(a->vertexList[i]);
}
free(a);
}
EdgePtr createEdgeNode(VTYPE v, VTYPE w, int weight) {
EdgePtr a = (EdgePtr)malloc(sizeof(Edge));
memset(a, 0, sizeof(Edge));
a->v = v;
a->w = w;
a->weight = weight;
a->next = NULL;
return a;
}
int getVertexPos(const AdjGraphPtr m, const VTYPE key) {
for (int i = 0; i < m->vNum; i++) {
if (m->vertexList[i]->v == key)
return i;
}
return -1;
}
void insertVertex(AdjGraphPtr adj, const VTYPE key) {
for (int i = 0; i < adj->vNum; i++) {
if (adj->vertexList[i] == key)
return;
}
VertexPtr vNode = (VertexPtr)malloc(sizeof(Vertex));
memset(vNode, 0, sizeof(Vertex));
vNode->v = key;
vNode->firstEdge = NULL;
adj->vertexList[adj->vNum] = vNode;
adj->vNum++;
}
void insertEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight) {
int pos_a = getVertexPos(adj, a);
EdgePtr avex = createEdgeNode(a, b, weight);
if (adj->vertexList[pos_a]->firstEdge == NULL) {
adj->vertexList[pos_a]->firstEdge = avex;
}
else {
EdgePtr tmp = adj->vertexList[pos_a]->firstEdge;
while (tmp->next != NULL) {
tmp = tmp->next;
}
tmp->next = avex;
}
}
void addEdge(AdjGraphPtr adj, VTYPE a, VTYPE b, int weight) {
insertEdge(adj, a, b, weight);
insertEdge(adj, b, a, weight);
adj->eNum++;
}
AdjGraphPtr createAdjGraph(VTYPE *v, int v_length, int *e, int e_length_1, int e_length_2) {
AdjGraphPtr adj = (AdjGraphPtr)malloc(sizeof(AdjGraph));
memset(adj, 0, sizeof(AdjGraph));
for (int i = 0; i < v_length; i++) {
insertVertex(adj, v[i]);
}
for (int i = 0; i < e_length_1; i++) {
VTYPE a = *(e + e_length_2 * i + 0);
VTYPE b = *(e + e_length_2 * i + 1);
int weight = *(e + e_length_2 * i + 2);
addEdge(adj, a, b, weight);
}
return adj;
}
void print(int *a, int length) {
for (int i = 0; i < length; i++) {
printf("%d ", a[i]);
}
putchar('\n');
}
QueuePtr createQueue() {
QueuePtr q = (QueuePtr)malloc(sizeof(Queue));
memset(q, 0, sizeof(Queue));
q->size = 0;
q->head = q->tail = NULL;
return q;
}
QueueNodePtr createQueueNode(QUETYPE key) {
QueueNodePtr q = (QueueNodePtr)malloc(sizeof(Queue));
memset(q, 0, sizeof(Queue));
q->next = NULL;
q->data = key;
return q;
}
void insertQueue(QueuePtr q, QUETYPE key, int isSort) {
QueueNodePtr a = createQueueNode(key);
if (q->head == NULL) { //notice
q->head = q->tail = a;
}
else {
q->tail->next = a;
q->tail = a;
}
q->size++;
if (isSort == 1) //自动排序
queueSort(q);
}
QUETYPE outQueue(QueuePtr q) {
QUETYPE v;
if (q->head != NULL) { //notice
v = q->head->data;
QueueNodePtr tmp = q->head->next;
free(q->head);
q->head = tmp;
}
else {
v = NULL;
}
return v;
}
void deleteQueue(QueuePtr q) {
if (q->head != NULL) { //notice
QueueNodePtr tmp = q->head;
QueueNodePtr tmp2;
while (tmp != NULL) {
tmp2 = tmp;
tmp = tmp->next;
free(tmp2);
}
}
free(q);
}
int isQueueEmpty(QueuePtr q) {
if (q->head == q->tail) {
return TRUE;
}
return FALSE;
}