普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树。意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小。该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现;并在1957年由美国计算机科学家罗伯特·普里姆(英语:Robert C. Prim)独立发现;1959年,艾兹格·迪科斯彻再次发现了该算法。因此,在某些场合,普里姆算法又被称为DJP算法、亚尔尼克算法或普里姆-亚尔尼克算法。
算法描述:
1).输入:一个加权连通图,其中顶点集合为V,边集合为E;
2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;
3).重复下列操作,直到Vnew = V:
a.在集合E中选取权值最小的边<u, v>,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);
b.将v加入集合Vnew中,将<u, v>边加入集合Enew中;
4).输出:使用集合Vnew和Enew来描述所得到的最小生成树。
prim算法函数如下
void Prim(Graph G)
{
int v=0;//初始节点
closedge C[MaxVerNum];
int mincost = 0; //记录最小生成树的各边权值之和
//初始化
for (int i = 0; i < G.vexnum; i++)
{
C[i].adjvex = v;
C[i].lowcost = G.Edge[v][i];
}
cout << "最小生成树的所有边:"<< endl;
//初始化完毕,开始G.vexnum-1次循环
for (int i = 1; i < G.vexnum; i++)
{
int k;
int min = INF;
//求出与集合U权值最小的点 权值为0的代表在集合U中
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && C[j].lowcost<min)
{
min = C[j].lowcost;
k = j;
}
}
//输出选择的边并累计权值
cout << "(" << G.Vex[k] << "," << G.Vex[C[k].adjvex]<<") ";
mincost += C[k].lowcost;
//更新最小边
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && G.Edge[k][j]<C[j].lowcost)
{
C[j].adjvex = k;
C[j].lowcost= G.Edge[k][j];
}
}
}
cout << "最小生成树权值之和:" << mincost << endl;
}
所有代码:
#include <stdio.h>
#include<stdlib.h>
#include <string.h>
#define MaxVerNum 100
#define MaxVertexNum 100
#define INF 32767
typedef int VertexType; //顶点的数据类型
typedef int EdgeType; //带权图中边上权值的数据类型
typedef struct closedge
{
int adjvex; //最小边在集合U(最小边在当前子树顶点集合中的那个顶点的下标)
int lowcost; //最小边上的权值
};
typedef struct
{
VertexType Vex[MaxVertexNum]; //顶点表
EdgeType Edge[MaxVertexNum][MaxVertexNum]; //邻接矩阵,边表
int vexnum, edgenum; //图的顶点数和弧数
}MGraph;
/***********创建有向图*·***********/
void create_Graph2(MGraph *G)
{
int i, j;
int start, end; //边的起点序号、终点序号
int numV, numE;
int w; //边上的权值
printf("请输入所创建有向图的顶点数和边数(用空格隔开):");
scanf("%d%d", &numV, &numE);
G->vexnum = numV;
G->edgenum = numE;
printf("\n");
//图的初始化
for (i = 0; i < G->vexnum; i++)
{
for (j = 0; j < G->vexnum; j++)
{
if (i == j)
G->Edge[i][j] = 0;
else
G->Edge[i][j] = 32767;
}
}
//顶点信息存入顶点表
for (i = 0; i < G->vexnum; i++)
{
printf("请输入第%d个顶点的信息:", i + 1);
scanf("%d", &G->Vex[i]);
}
printf("\n");
//输入无向图边的信息
for (i = 0; i < G->edgenum; i++)
{
printf("请输入边的起点序号,终点序号,权值(用空格隔开):");
scanf("%d%d%d", &start, &end, &w);
G->Edge[start - 1][end - 1] = w; //有向图只在这里不一样
G->Edge[end - 1][start - 1] = w;
}
}
void Prim(MGraph G)
{
int v=0;//初始节点
closedge C[MaxVerNum];
int mincost = 0; //记录最小生成树的各边权值之和
//初始化
for (int i = 0; i < G.vexnum; i++)
{
C[i].adjvex = v;
C[i].lowcost = G.Edge[v][i];
}
//初始化完毕,开始G.vexnum-1次循环
for (int i = 1; i < G.vexnum; i++)
{
int k;
int min = INF;
//求出与集合U权值最小的点 权值为0的代表在集合U中
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && C[j].lowcost<min)
{
min = C[j].lowcost;
k = j;
}
}
//输出选择的边并累计权值
mincost += C[k].lowcost;
//更新最小边
for (int j = 0; j<G.vexnum; j++)
{
if (C[j].lowcost != 0 && G.Edge[k][j]<C[j].lowcost)
{
C[j].adjvex = k;
C[j].lowcost= G.Edge[k][j];
}
}
}
printf("最小生成树权值之和");
printf("%d",mincost);
}
int main(){
MGraph G;
create_Graph2(&G);
Prim(G);
return 0;
}