无向网的最小生成树——Prim算法(转)

适用于边较为稠密型的连通网

假设N=(V,{VR})是一个连通网,TE是V上的最小生成树的边的集合,Prim算法从U={u0}(u0∈V) ,TE={}开始,重复执行下面的操作:

在所以的u∈U,v∈V-U的边(u,v)∈VR中找一条权值最小的边(u0,v0),并入集合TE,同时v0并入U,直至U=V为止。此时TE中必有n-1条边,则T=(V,{TE})为N的最小生成树。为了实现这个算法,需引入一个辅助的数组closedge,记录从U到V-U具有最小权值的边,每个顶点Vi对应数组中的closedege[i-1],包括两个域,一个是adjvex,存储该边依附的在U中顶点,另一个是Lowcost,存储权值。下面是代码:

tree.h文件

  1 #include<iostream>
2 #include<string>
3 #include<queue>
4 using namespace std;
5
6 const int MAX_VEX_NUM=20;
7 bool visited[20];
8
9 class MGraph;
10
11 class CS_Tree;
12 //树结点
13 class CSnode
14 {
15 string data;
16 CSnode *firstchild;
17 CSnode *nextsibling;
18 friend class CS_Tree;
19 friend class MGraph;
20 };
21
22 //树类定义
23 class CS_Tree
24 {
25 public:
26 void PreRoot_Traverse(CSnode *T) //先根遍历
27 {
28 if(T)
29 {
30 cout<<T->data<<" ";
31 PreRoot_Traverse(T->firstchild);
32 PreRoot_Traverse(T->nextsibling);
33 }
34 }
35
36 void PostRoot_Traverse(CSnode *T) //后根遍历
37 {
38 if(T)
39 {
40 PostRoot_Traverse(T->firstchild);
41 cout<<T->data<<" ";
42 PostRoot_Traverse(T->nextsibling);
43 }
44 }
45
46 void LevelOrder_Traverse(CSnode *T) //层次遍历
47 {
48 queue<CSnode *> q;
49 CSnode *t;
50 q.push(T);
51 do
52 {
53 t=q.front();
54 do
55 {
56 cout<<t->data<<" ";
57 if(t->firstchild)
58 q.push(t->firstchild);
59 t=t->nextsibling;
60 }while(t);
61 q.pop();
62 }while(!q.empty());
63 }
64 };
65
66
67 class ArcNode
68 {
69 public:
70 int adj;
71 bool search;//作为建立最小生成树的时结点是否被访问过的标志
72 friend class MGraph;
73 };
74
75 class temp //为建树所用
76 {
77 public:
78 int i;
79 int j;
80 int weight;
81 friend class MGraph;
82 };
83
84 class closedge //为建树所用
85 {
86 public:
87 string adjvex;
88 int weight;
89 friend class MGraph;
90 };
91
92 class MGraph
93 {
94 private:
95 string vexs[MAX_VEX_NUM];//顶点数组
96 ArcNode arcs[MAX_VEX_NUM][MAX_VEX_NUM];//邻接矩阵
97 int vexnum;//顶点数
98 int arcnum;//边数
99 public:
100 void Create_MG()
101 {
102 int i,j,k;
103 cout<<"输入图的顶点数和边数:";
104 cin>>vexnum>>arcnum;
105 cout<<"输入各个顶点的民称:";
106 for(i=0;i<vexnum;i++)
107 cin>>vexs[i];
108
109 for(i=0;i<vexnum;i++)
110 for(int j=0;j<vexnum;j++)
111 {
112 arcs[i][j].adj=-1;
113 arcs[i][j].search=false;
114 }
115 //上面是初始化邻接矩阵
116
117 for(k=0;k<arcnum;k++)
118 {
119 cout<<"输入每条边对应的两个顶点以及该边的权值:";
120 string v1,v2;
121 int w;
122 cin>>v1>>v2>>w;
123 i=Locate_Vex(v1);
124 j=Locate_Vex(v2);
125
126 while(i<0|| i>vexnum-1 || j<0 || j>vexnum-1)
127 {
128 cout<<"结点位置输入错误,重新输入: ";
129 cin>>v1>>v2>>w;
130 i=Locate_Vex(v1);
131 j=Locate_Vex(v2);
132 }
133
134 arcs[i][j].adj=w;
135 arcs[j][i]=arcs[i][j]; //置对称边
136 }
137 cout<<"图构造完成"<<endl;
138 }
139
140 int Locate_Vex(string x) //用于确定顶点在顶点数组中的位置
141 {
142 for(int k=0;vexs[k]!=x;k++);
143 return k;
144 }
145
146 void DFS(int v)
147 {
148 visited[v]=true;
149 cout<<vexs[v]<<" ";
150
151 for(int j=0;j<vexnum;j++)
152 if(arcs[v][j].adj!=-1 && !visited[j])
153 DFS(j);
154 }
155
156 //深度优先遍历图
157 void DFS_Traverse()
158 {
159 //visited数组用来作为是否已访问的标志
160 for(int i=0;i<vexnum;i++)
161 visited[i]=false;
162
163 for(int v=0;v<vexnum;v++)
164 if(!visited[v])
165 DFS(v);
166 }
167
168 //广度优先遍历
169 void BFS_Traverse()
170 {
171 queue<int> q;
172 int u,w,v;
173 for(v=0;v<vexnum;v++)
174 visited[v]=false;
175
176 for(v=0;v<vexnum;v++)
177 {
178 if(!visited[v])
179 {
180 visited[v]=true;
181 cout<<vexs[v]<<" ";
182 q.push(v);
183
184 while(!q.empty())
185 {
186 u=q.front();
187 q.pop();
188
189 for(w=0;w<vexnum;w++)
190 {
191 if(arcs[u][w].adj!=-1 && !visited[w])
192 {
193 visited[w]=true;
194 cout<<vexs[w]<<" ";
195 q.push(w);
196 }
197 }
198 }
199 }
200 }
201 }
202
203 CSnode *Prim_Min_Pre(int v,closedge c[])
204 {
205 string *u=new string[20];//U中保存已经作为树结点的顶点
206 int num=0;
207 int low;
208 for(int l=0;l<vexnum;l++) //作为顶点是否已经在U中的标志
209 visited[l]=false;
210 u[num]=vexs[v];
211 visited[v]=true;
212 num++;
213
214 for(int vex=1;vex<vexnum;vex++)
215 {
216 temp min;
217 low=99999;
218 /*--------------------------------
219 /下面的for循环是找出和U中所有顶点
220 /相邻的非U中的顶点中边权值最小的一条
221 /--------------------------------*/
222 for(int j=0;j<num;j++)
223 {
224 int i=Locate_Vex(u[j]);
225 for(int k=0;k<vexnum;k++)
226 {
227 if(arcs[i][k].adj!=-1 && arcs[i][k].adj<low && !arcs[i][k].search &&!visited[k])
228 {
229
230 low=arcs[i][k].adj;
231 min.i=i;
232 min.j=k;
233 min.weight=low;
234 }
235 }
236 }
237 c[min.j].adjvex=vexs[min.i];
238 c[min.j].weight=min.weight;
239 u[num]=vexs[min.j];
240 num++;
241 visited[min.j]=true;
242 arcs[min.i][min.j].search=arcs[min.j][min.i].search=true; //将已经作为树的边的边标志为已访问
243 if(num==vexnum)
244 break ;
245 }
246 CSnode *T=NULL;
247 Prim_TREE(v,T,c);//将c数组中的元素转换为树
248 return T;
249
250 }
251
252 void Prim_TREE(int v,CSnode *&T,closedge c[])
253 {
254 CSnode *r=NULL,*p=NULL,*q=NULL;
255 bool first;
256 queue<CSnode *> qu;
257 bool connected[20];//作为该顶点是否已经连接在树中的标志
258 for(int i=0;i<vexnum;i++)
259 connected[i]=false;
260
261 qu.push(T);
262
263 //下面是建树过程,总体思路和图的广度优先生成树的是一样的
264 while(!qu.empty())
265 {
266 r=qu.front();
267 qu.pop();
268
269 if(!r && !T)//第一次进来时,T为空
270 {
271 T=new CSnode;
272 T->data=vexs[v];
273 T->firstchild=T->nextsibling=NULL;
274 r=T;
275 }
276
277 first=true;
278 for(int i=1;i<vexnum;i++)
279 {
280 if(!connected[i] && c[i].adjvex==r->data)
281 {
282 connected[i]=true;
283 p=new CSnode;
284 p->data=vexs[i];
285 p->firstchild=p->nextsibling=NULL;
286 qu.push(p);
287 if(first)
288 {
289 r->firstchild=p;
290 first=false;
291 }
292 else
293 q->nextsibling=p;
294 q=p;
295 }
296 }
297 }
298 }
299 };

  测试函数main.cpp

 1 #include"tree.h"
2
3 int main()
4 {
5 MGraph G;
6 G.Create_MG();
7
8 cout<<"图的深度优先遍历为:";
9 G.DFS_Traverse();
10 cout<<endl;
11
12 cout<<"图的广度优先遍历为:";
13 G.BFS_Traverse();
14 cout<<endl;
15
16 cout<<"要从哪个顶点开始建立最小生成树:";
17 int v;
18 cin>>v;
19
20 closedge c[20];
21 CSnode *T=NULL;
22 T=G.Prim_Min_Pre(v,c);
23 CS_Tree root;
24 cout<<"建树完成"<<endl;
25
26 cout<<"最小生成树的先根遍历为:";
27 root.PreRoot_Traverse(T);
28 cout<<endl;
29
30 cout<<"最小生成树的后根遍历为:";
31 root.PostRoot_Traverse(T);
32 cout<<endl;
33
34 cout<<"最小生成树的层次遍历为:";
35 root.LevelOrder_Traverse(T);
36 cout<<endl;
37
38 return 0;
39 }

  下面是测试结果

 1 输入图的顶点数和边数:9 15
2 输入各个顶点的民称:v1 v2 v3 v4 v5 v6 v7 v8 v9
3 输入每条边对应的两个顶点以及该边的权值:v1 v6 7
4 输入每条边对应的两个顶点以及该边的权值:v1 v7 3
5 输入每条边对应的两个顶点以及该边的权值:v1 v2 2
6 输入每条边对应的两个顶点以及该边的权值:v2 v7 6
7 输入每条边对应的两个顶点以及该边的权值:v2 v3 4
8 输入每条边对应的两个顶点以及该边的权值:v6 v5 6
9 输入每条边对应的两个顶点以及该边的权值:v6 v9 5
10 输入每条边对应的两个顶点以及该边的权值:v7 v9 1
11 输入每条边对应的两个顶点以及该边的权值:v7 v8 3
12 输入每条边对应的两个顶点以及该边的权值:v3 v8 2
13 输入每条边对应的两个顶点以及该边的权值:v3 v4 2
14 输入每条边对应的两个顶点以及该边的权值:v9 v5 2
15 输入每条边对应的两个顶点以及该边的权值:v9 v8 4
16 输入每条边对应的两个顶点以及该边的权值:v8 v4 8
17 输入每条边对应的两个顶点以及该边的权值:v5 v4 1
18 图构造完成
19 图的深度优先遍历为:v1 v2 v3 v4 v5 v6 v9 v7 v8
20 图的广度优先遍历为:v1 v2 v6 v7 v3 v5 v9 v8 v4
21 要从哪个顶点开始建立最小生成树:0
22 建树完成
23 最小生成树的先根遍历为:v1 v2 v7 v9 v5 v4 v3 v8 v6
24 最小生成树的后根遍历为:v2 v8 v3 v4 v5 v6 v9 v7 v1
25 最小生成树的层次遍历为:v1 v2 v7 v9 v5 v6 v4 v3 v8
26 Press any key to continue

  按照输入所生成的无向网和生成树如下








转载于:https://www.cnblogs.com/math2010/archive/2011/08/17/2142083.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Prim算法求给定无向连通网的最小生成树的示例代码: ```c #define MAXV 100 // 最大顶点数 #define INF 0x3f3f3f3f // 无穷大 int G[MAXV][MAXV]; // 存储图的邻接矩阵 int d[MAXV]; // 存储到已选顶点集合的最小距离 int parent[MAXV]; // 存储每个顶点最小生成树中的父节点 bool visited[MAXV]; // 标记顶点是否已被加入最小生成树中 int prim(int n, int start) { int sum = 0; memset(visited, false, sizeof(visited)); memset(d, INF, sizeof(d)); memset(parent, -1, sizeof(parent)); d[start] = 0; for (int i = 0; i < n; i++) { int u = -1; for (int j = 0; j < n; j++) { if (!visited[j] && (u == -1 || d[j] < d[u])) { u = j; } } visited[u] = true; sum += d[u]; for (int v = 0; v < n; v++) { if (!visited[v] && G[u][v] != INF && G[u][v] < d[v]) { d[v] = G[u][v]; parent[v] = u; } } } return sum; } ``` 其中,`G`是一个二维数组,存储无向连通网的邻接矩阵;`d`是一个一维数组,存储到已选顶点集合的最小距离;`parent`是一个一维数组,存储每个顶点最小生成树中的父节点;`visited`是一个一维数组,标记顶点是否已被加入最小生成树中。`n`是无向连通网的顶点数,`start`是起始顶点。 代码中,首先初始化各数组。然后从起始顶点开始,遍历所有顶点,选择距离已选顶点集合最近的未加入最小生成树顶点,将其加入最小生成树,并更新与该顶点相邻的未加入最小生成树顶点的最小距离和父节点。最后返回最小生成树权值和。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值