prime算法求最小生成树——邻接矩阵(c语言详细代码)(附带案例)

文章介绍了Prime算法,它是解决图论中构建最小生成树问题的一种方法。基于切分定理,算法逐步选择横切边并将顶点添加到已选集合,直至所有顶点都在集合中。文中还提供了C语言实现的邻接矩阵来表示图,并展示了如何初始化和遍历图以执行Prime算法。
摘要由CSDN通过智能技术生成

Prime算法是解决最小生成树的一个算法。
不管是Kruskal算法还是Prime算法去,其核心都是切分定理。
切分定理:
切分
把图中的节点分为两部分,称为一个切分(Cut);
横切边
如果一个边的两个端点,属于切分(Cut)不同的两边,这个边称为横切边(Crossing Edge);
切分定理
给定任意切分,横切边中权值最小的边必然属于最小生成树;

切分定理的证明可使用反正法,这里不给出证明,证明不了只需要知道切分定理的正确性就能推出Prime算法的正确性。

Prime算法:
Prime算法是切分定理的正向应用。首先选定一个顶点0,当选择一个顶点后就把它看成一个切分,一部分为已选顶点集合{0},另一部分为未选顶点集合{1,2,3,4,5}。那么此时会出现横切边{{0,1},{0,2},{0,3}}。

在这里插入图片描述

按照切分定理选择最小的边,{0,3}则为最小生成树的一条边。此时将{0,3}这条边加入到存储最小生成树的数据结构中,将顶点3加入到已选集合中{0,3},未选集合为{2,4,5},这时横向边集合也发生了改变{{0,2},{0,1},{0,3},{3,7},{3,5},{3,4}}。
在这里插入图片描述

可以观察到{0,3}这条边刚刚已经加入到最小生成树中了,已经不再是横向边了。
此时可按照切分定理,选择最小的横向边,将边的另一点加入到已选集合中。
按照这个算法继续往下执行到如图所示:
在这里插入图片描述

可发现横向边从{0,2}从橙色变成了绿色。其实很好理解,已选集合{0,2,3,4}中的{0,2}那条边已经没有机会再被选择了,他们内部已经构成了局部最小生成树了。
按照这个算法,直到所有顶点都在已选集合中,算法就计算结束了。

总而言之,就是在找到的点和未找到的点的两个集合之间找到路劲最短的边,再将未找到的点放到已经找到的点的集合里

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#define N  100
#define max 1000;
//无向图邻接矩阵数据结构 
typedef  struct {
    int vcount;//顶点数
    char  vexs[N];     // 顶点信息
    int  arcs[N][N]; //带权关系矩阵,就是边
} GraphMatrix;

int letter2num(char letter) {
    return letter - 65;
}
char num2letter(int num) {
    return num + 65;
}//数字转字母,字母转数字

//void printGraph(GraphMatrix* G)
//{//本函数输出图的邻接矩阵
//    int i, j;
//    for (i = 0; i < G->vcount; i++)
//    {
//        printf("顶点%c      ", G->vexs[i]);
//        for (j = 0; j < G->vcount; j++)
//            printf("%d ", G->arcs[i][j]);
//        printf("\n");
//    }
//}

/*图初始化-邻接矩阵
*/
GraphMatrix* initGraphMatrix()
{
    GraphMatrix* G;
    G = (GraphMatrix*)malloc(sizeof(GraphMatrix));
    int v, s;
    printf("请输入定点数和边数: ");
    scanf("%d %d",&v, &s);
    G->vcount = v;
    int i, j;
    getchar();                  //吸收回车
    printf("请输入定点名称: ");
    for (i = 0; i < v; i++)     //创建矩阵
    {
        scanf("%c", &G->vexs[i]);
        getchar();              //吸收回车
        for (j = 0; j < v; j++)
        {
            G->arcs[i][j] = max;//初始化矩阵
        }
    }
    char str_s, str_e;
    int start,end,length;

    for (i = 0; i < s; i++) {
        printf("请输入边%d的顶点1,2和长度: ",i+1);
        scanf("%c %c %d", &str_s, &str_e,&length);
        getchar();              //吸收回车
        start =letter2num(str_s);
        end =letter2num(str_e);
        G->arcs[start][end] = length;
        G->arcs[end][start] = length;
    }
    return G;
}
void prime(GraphMatrix* G,char a) {
    int v=letter2num(a);        //起始点

    int visit[N]={0};           //点是否访问过,未访问0,访问1
    visit[v] = 1;               //起始点已经访问过

    int short_length[N];        //访问过的点的集合到未访问过点的集合的最小路径

    for (int i = 0; i < G->vcount; i++) 
        short_length[i] = G->arcs[v][i];        //初始化,此时只有起始点
    short_length[v] = 0;        //起始点路径为0

    printf("%c", a);
    for (int i = 0; i < G->vcount-1; i++)       //有n个点,那最短路径就是n-1个边组成
    {
        int min_length = max;
        int k=0;
        for (int j = 0; j < G->vcount; j++) {   //在所有未访问过的边里找到最短路径
            if (min_length > short_length[j] && visit[j] == 0) {
                k = j;
                min_length = short_length[j];
            }
        }
        visit[k] = 1;//将这个点加入已访问过的集合
        printf("-%c", num2letter(k));
        for (int j = 0; j < G->vcount; j++) {
            if (visit[j] == 0 && short_length[j] > G->arcs[k][j])
                short_length[j] = G->arcs[k][j];
        }//更新未访问过点的最短路径

    }
    return;
}
int main()
{

    GraphMatrix* G = initGraphMatrix();
    //printGraph(G);
    char a;
    //scanf("请输入起始点%c", &a);
    a = 'A';                    //假设起始点为A
    prime(G,a);
}

案例:

 
6 10
A B C D E F
A B 5
A C 6
A D 4
B C 1
B D 2
C D 2
C E 5
C F 3
D F 4
F E 4

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值