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

本文介绍了最小生成树的概念,并详细讲解了C语言中Prim和Kruskal算法的实现原理、证明过程及代码示例,通过贪心算法探讨了这两种经典算法在加权无向图中的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最小生成树简介

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

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

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

原理

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

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

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

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

Prim算法

算法实现

这里写图片描述
1)选择v3为起点,与v3连接的边中,权值最小的边为5,连接的点位v1, 框选除v1和v3作为一个整体
2)在v1,v3的整体中,与之连接的边中,权值最小的边为4,连接的点位v2,框选v1,v2,v3作为一个整体
3)重复以上步骤,直到遍历了所有的节点。

算法证明

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

  1. 图是一棵树
    这里写图片描述
  2. 图是一个环
    这里写图片描述
  3. 图是环和树的简单连接
    这里写图片描述

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

代码实现 1

基本思路是将所有顶点放入一张新的图,然后再进行Prim算法,寻找节点的最小权重edge,再将数据放入新的图中。
ADT:

#include <stdio.h>
#include <malloc.h>
#include <memory.h>
#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 <stdio.h>
#include <malloc.h>
#include <memory.h>
#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; i<length; i++){
        if (*(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 = <
Prim算法Kruskal算法都是用来解决最小生成树问题算法。下面是用C语言实现Prim算法Kruskal算法的示例代码。 ## Prim算法 ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> #define V 5 int minKey(int key[], int mstSet[]) { int min = INT_MAX, min_index; for (int v = 0; v < V; v++) if (mstSet[v] == 0 && key[v] < min) min = key[v], min_index = v; return min_index; } int printMST(int parent[], int n, int graph[V][V]) { printf("Edge \tWeight\n"); for (int i = 1; i < V; i++) printf("%d - %d \t%d \n", parent[i], i, graph[i][parent[i]]); } void primMST(int graph[V][V]) { int parent[V]; int key[V]; int mstSet[V]; for (int i = 0; i < V; i++) key[i] = INT_MAX, mstSet[i] = 0; key[0] = 0; parent[0] = -1; for (int count = 0; count < V-1; count++) { int u = minKey(key, mstSet); mstSet[u] = 1; for (int v = 0; v < V; v++) if (graph[u][v] && mstSet[v] == 0 && graph[u][v] < key[v]) parent[v] = u, key[v] = graph[u][v]; } printMST(parent, V, graph); } int main() { int graph[V][V] = {{0, 2, 0, 6, 0}, {2, 0, 3, 8, 5}, {0, 3, 0, 0, 7}, {6, 8, 0, 0, 9}, {0, 5, 7, 9, 0}, }; primMST(graph); return 0; } ``` ## Kruskal算法 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #define V 5 #define E 9 struct Edge { int src, dest, weight; }; struct Graph { int V, E; struct Edge* edge; }; struct Graph* createGraph(int V, int E) { struct Graph* graph = (struct Graph*) malloc( sizeof(struct Graph) ); graph->V = V; graph->E = E; graph->edge = (struct Edge*) malloc( graph->E * sizeof( struct Edge ) ); return graph; } struct subset { int parent; int rank; }; int find(struct subset subsets[], int i) { if (subsets[i].parent != i) subsets[i].parent = find(subsets, subsets[i].parent); return subsets[i].parent; } void Union(struct subset subsets[], int x, int y) { int xroot = find(subsets, x); int yroot = find(subsets, y); if (subsets[xroot].rank < subsets[yroot].rank) subsets[xroot].parent = yroot; else if (subsets[xroot].rank > subsets[yroot].rank) subsets[yroot].parent = xroot; else { subsets[yroot].parent = xroot; subsets[xroot].rank++; } } int myComp(const void* a, const void* b) { struct Edge* a1 = (struct Edge*)a; struct Edge* b1 = (struct Edge*)b; return a1->weight > b1->weight; } void KruskalMST(struct Graph* graph) { int V = graph->V; struct Edge result[V]; int e = 0; int i = 0; qsort(graph->edge, graph->E, sizeof(graph->edge[0]), myComp); struct subset *subsets = (struct subset*) malloc( V * sizeof(struct subset) ); for (int v = 0; v < V; ++v) { subsets[v].parent = v; subsets[v].rank = 0; } while (e < V - 1 && i < graph->E) { struct Edge next_edge = graph->edge[i++]; int x = find(subsets, next_edge.src); int y = find(subsets, next_edge.dest); if (x != y) { result[e++] = next_edge; Union(subsets, x, y); } } printf("Following are the edges in the constructed MST\n"); for (i = 0; i < e; ++i) printf("%d -- %d == %d\n", result[i].src, result[i].dest, result[i].weight); return; } int main() { int V = 5; // Number of vertices in graph int E = 9; // Number of edges in graph struct Graph* graph = createGraph(V, E); // add edge 0-1 graph->edge[0].src = 0; graph->edge[0].dest = 1; graph->edge[0].weight = 4; // add edge 0-2 graph->edge[1].src = 0; graph->edge[1].dest = 2; graph->edge[1].weight = 4; // add edge 1-2 graph->edge[2].src = 1; graph->edge[2].dest = 2; graph->edge[2].weight = 2; // add edge 2-3 graph->edge[3].src = 2; graph->edge[3].dest = 3; graph->edge[3].weight = 3; // add edge 2-4 graph->edge[4].src = 2; graph->edge[4].dest = 4; graph->edge[4].weight = 1; // add edge 3-4 graph->edge[5].src = 3; graph->edge[5].dest = 4; graph->edge[5].weight = 2; // add edge 1-4 graph->edge[6].src = 1; graph->edge[6].dest = 4; graph->edge[6].weight = 5; // add edge 1-3 graph->edge[7].src = 1; graph->edge[7].dest = 3; graph->edge[7].weight = 6; // add edge 0-3 graph->edge[8].src = 0; graph->edge[8].dest = 3; graph->edge[8].weight = 5; KruskalMST(graph); return 0; } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值