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

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

最小生成树简介

最小生成树(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 = <
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值