普里姆(Prim)算法,和克鲁斯卡尔算法一样,是用来求加权连通图的最小生成树的算法。

基本思想
对于图G而言,V是所有顶点的集合;现在,设置两个新的集合U和T,其中U用于存放G的最小生成树中的顶点,T存放G的最小生成树中的边。 从所有uЄU,vЄ(V-U) (V-U表示出去U的所有顶点)的边中选取权值最小的边(u, v),将顶点v加入集合U中,将边(u, v)加入集合T中,如此不断重复,直到U=V为止,最小生成树构造完毕,这时集合T中包含了最小生成树中的所有边。

普里姆算法图解

img

以上图G4为例,来对普里姆进行演示(从第一个顶点A开始通过普里姆算法生成最小生成树)。

img

初始状态:V是所有顶点的集合,即V={A,B,C,D,E,F,G};U和T都是空!
第1步:将顶点A加入到U中。
此时,U={A}。
第2步:将顶点B加入到U中。
上一步操作之后,U={A}, V-U={B,C,D,E,F,G};因此,边(A,B)的权值最小。将顶点B添加到U中;此时,U={A,B}。
第3步:将顶点F加入到U中。
上一步操作之后,U={A,B}, V-U={C,D,E,F,G};因此,边(B,F)的权值最小。将顶点F添加到U中;此时,U={A,B,F}。
第4步:将顶点E加入到U中。
上一步操作之后,U={A,B,F}, V-U={C,D,E,G};因此,边(F,E)的权值最小。将顶点E添加到U中;此时,U={A,B,F,E}。
第5步:将顶点D加入到U中。
上一步操作之后,U={A,B,F,E}, V-U={C,D,G};因此,边(E,D)的权值最小。将顶点D添加到U中;此时,U={A,B,F,E,D}。
第6步:将顶点C加入到U中。
上一步操作之后,U={A,B,F,E,D}, V-U={C,G};因此,边(D,C)的权值最小。将顶点C添加到U中;此时,U={A,B,F,E,D,C}。
第7步:将顶点G加入到U中。
上一步操作之后,U={A,B,F,E,D,C}, V-U={G};因此,边(E,G)的权值最小。将顶点G添加到U中;此时,U=V。

此时,最小生成树构造完成!它包括的顶点依次是:A B F E D C G

普里姆算法的代码说明

以”邻接矩阵”为例对普里姆算法进行说明,对于”邻接表”实现的图在后面会给出相应的源码。

1. 基本定义

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 邻接矩阵
typedef struct _graph
{
char vexs[MAX]; // 顶点集合
int vexnum; // 顶点数
int edgnum; // 边数
int matrix[MAX][MAX]; // 邻接矩阵
}Graph, *PGraph;

// 边的结构体
typedef struct _EdgeData
{
char start; // 边的起点
char end; // 边的终点
int weight; // 边的权重
}EData;

复制代码

Graph是邻接矩阵对应的结构体。
vexs用于保存顶点,vexnum是顶点数,edgnum是边数;matrix则是用于保存矩阵信息的二维数组。例如,matrix[i][j]=1,则表示”顶点i(即vexs[i])”和”顶点j(即vexs[j])”是邻接点;matrix[i][j]=0,则表示它们不是邻接点。
EData是邻接矩阵边对应的结构体。

2. 普里姆算法

复制代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
#include<stdio.h>
#include<stdlib.h>
#include<malloc.h>
#include<string.h>
#define MAX 100
#define INF (~(0x1<<31))
typedef struct Graph
{
char vexs[MAX];
int vexnum;
int edgnum;
int matrix[MAX][MAX];
} Graph,*PGraph;

typedef struct EdgeData
{
char start;
char end;
int weight;
} EData;

static int get_position(Graph g,char ch)
{
int i;
for(i=0; i<g.vexnum; i++)
if(g.vexs[i]==ch)
return i;
return -1;
}

Graph* create_graph()
{
char vexs[]= {'A','B','C','D','E','F','G'};
int matrix[][7]=
{
{0,12,INF,INF,INF,16,14},
{12,0,10,INF,INF,7,INF},
{INF,10,0,3,5,6,INF},
{INF,INF,3,0,4,INF,INF},
{INF,INF,5,4,0,INF,8},
{16,7,6,INF,2,0,9},
{14,INF,INF,INF,8,9,0}
};
int vlen=sizeof(vexs)/sizeof(vexs[0]);
int i,j;
Graph *pG;
if((pG=(Graph*)malloc(sizeof(Graph)))==NULL)
return NULL;
memset(pG,0,sizeof(pG));
pG->vexnum=vlen;
for(i=0; i<pG->vexnum; i++)
pG->vexs[i]=vexs[i];
for(i=0; i<pG->vexnum; i++)
for(j=0; j<pG->vexnum; j++)
pG->matrix[i][j]=matrix[i][j];
for(i=0; i<pG->vexnum; i++)
{
for(j=0; j<pG->vexnum; j++)
{
if(i!=j&&pG->matrix[i][j]!=INF)
pG->edgnum++;
}
}
pG->edgnum/=2;
return pG;
}

void print_graph(Graph G)
{
int i,j;
printf("Matrix Graph: \n");
for(i=0; i<G.vexnum; i++)
{
for(j=0; j<G.vexnum; j++)
printf("%10d ",G.matrix[i][j]);
printf("\n");
}
}

EData* get_edges(Graph G)
{
EData *edges;
edges=(EData*)malloc(G.edgnum*sizeof(EData));
int i,j;
int index=0;
for(i=0; i<G.vexnum; i++)
{
for(j=i+1; j<G.vexnum; j++)
{
if(G.matrix[i][j]!=INF)
{
edges[index].start=G.vexs[i];
edges[index].end=G.vexs[j];
edges[index].weight=G.matrix[i][j];
index++;
}
}
}
return edges;
}

void prim(Graph G,int start)
{
int min,i,j,k,m,n,sum;
int index=0;
char prim[MAX];
int weight[MAX];

prim[index++]=G.vexs[start];

for(i=0; i<G.vexnum; i++)
weight[i]=G.matrix[start][i];
weight[start]=0;

for(i=0; i<G.vexnum; i++)
{
//i用来控制循环的次数,每次加入一个结点,但是因为start已经加入,所以当i为start是跳过
if(start==i)
continue;
j=0;
k=0;
min=INF;
for(k=0; k<G.vexnum; k++)
{
if(weight[k]&&weight[k]<min)
{
min=weight[k];
j=k;
}
}
sum+=min;
prim[index++]=G.vexs[j];
weight[j]=0;
for(k=0; k<G.vexnum; k++)
{
if(weight[k]&&G.matrix[j][k]<weight[k])
weight[k]=G.matrix[j][k];
}
}
// 计算最小生成树的权值
sum = 0;
for (i = 1; i < index; i++)
{
min = INF;
// 获取prims[i]在G中的位置
n = get_position(G, prim[i]);
// 在vexs[0...i]中,找出到j的权值最小的顶点。
for (j = 0; j < i; j++)
{
m = get_position(G, prim[j]);
if (G.matrix[m][n]<min)
min = G.matrix[m][n];
}
sum += min;
}
printf("PRIM(%c)=%d: ", G.vexs[start], sum);
for (i = 0; i < index; i++)
printf("%c ", prim[i]);
printf("\n");
}

int main()
{
Graph *pG;
pG=create_graph();
print_graph(*pG);
prim(*pG,0);
}

复制代码

运行结果:

img

分类: 计算机算法