C语言 Prim算法和Kruskal算法的实现和证明

      • 最小生成树简介
        • 原理
      • Prim算法
        • 算法实现
        • 算法证明
        • 代码实现 1
        • 代码实现 2
      • Kruskal算法
        • 算法实现
        • 算法证明
        • 代码实现

最小生成树简介

最小生成树(MST):给定一加权无向图,找出它的一颗最小生成树。

定义:图的最小生成树是它的一副含有其所有顶点的无环连通子图。一副加权图的最小生成树是它的一颗权值(树种所有边的权值之和)最小的生成树。

我们约定:
1. 只考虑连通图
2. 边的权重不一定代表距离
3. 边的权重可能是0或者负数
4. 所有边的权重都各不相同。如果不同边的权重可以相同,最小生成树就不一定唯一了。存在多颗最小生成树的可能性会使部分算法的证明变得更加复杂。

原理

命题J(切分定理):在一副加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图中的最小生成树。
切分定理是解决最小生成树问题的所有算法的基础。更确切的说,这些算法都是贪心算法的特殊情况:使用切分定理找到最小生成树的一条边,不断重复直到找到最小生成树的所有边。

命题K(最小生成树的贪心算法):将含有V个顶点的任意加权连通图中属于最小生成树的边标记为黑色:初始状态下所有边均为灰色,找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了V-1条黑色边为止。

怎么理解切分定理呢?
Prim算法其实就是切分定理的应用,我们可以通过分析Prime算法,来理解切分定理。

我从链接数据结构(七)图了解了实现Prim算法的实现过程。

Prim算法

算法实现

C语言 Prim算法和Kruskal算法的实现和证明_第1张图片
1)选择v3为起点,与v3连接的边中,权值最小的边为5,连接的点位v1, 框选除v1和v3作为一个整体
2)在v1,v3的整体中,与之连接的边中,权值最小的边为4,连接的点位v2,框选v1,v2,v3作为一个整体
3)重复以上步骤,直到遍历了所有的节点。

算法证明

切分定理:在一副加权图中,给定任意的切分,它的横切边中的权重最小者必然属于图中的最小生成树。
为什么横切边权重最小者必然属于图的最小生成树呢?我们由简入繁。
1. 图没有环,即图是一棵树
2. 图是一个环
3. 图是环和树的简单连接

  1. 图是一棵树
    C语言 Prim算法和Kruskal算法的实现和证明_第2张图片
  2. 图是一个环
    C语言 Prim算法和Kruskal算法的实现和证明_第3张图片
  3. 图是环和树的简单连接
    C语言 Prim算法和Kruskal算法的实现和证明_第4张图片

所以对于更复杂的情况,无非是更多的环和更多的树的组合。
通过切分定理,我们就可以理解Prim算法了。

代码实现 1

基本思路是将所有顶点放入一张新的图,然后再进行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');
}

代码实现 2

代码 1 的实现比较复杂,另一种比较直观的方法则用到了数据结构(注:参考自《算法》4.3.4.1):

  • 顶点数组 marked[ ] ,是顶点索引的布尔数组
  • 队列 mst, 用于保存最小结构树的队列
  • 优先队列,用于保存横切边

C语言 Prim算法和Kruskal算法的实现和证明_第5张图片

我们可以重建图的结构,向图的边添加顶点, 相应的函数做出一些调整。
图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;
}

Kruskal算法

算法实现

C语言 Prim算法和Kruskal算法的实现和证明_第6张图片

算法证明

命题K(最小生成树的贪心算法):将含有V个顶点的任意加权连通图中属于最小生成树的边标记为黑色:初始状态下所有边均为灰色,找到一种切分,它产生的横切边均不为黑色。将它权重最小的横切边标记为黑色。反复,直到标记了V-1条黑色边为止。

讨论最简单的情况,最小权重边刚好成环,如下:
C语言 Prim算法和Kruskal算法的实现和证明_第7张图片
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;
}

你可能感兴趣的:(c,算法,图)