图->连通性->最小生成树(普里姆算法)

文字描述

  用连通网来表示n个城市及n个城市间可能设置的通信线路,其中网的顶点表示城市,边表示两城市之间的线路,赋于边的权值表示相应的代价。对于n个定点的连通网可以建立许多不同的生成树,每一棵生成树都可以是一个通信网。现在,我们要选择这样一个生成树,使总的耗费最少。这个问题就是构造连通网的最小代价生成树(Minimum Cost Spanning Tree: 最小生成树)的问题。一棵生成树的代价就是树上各边的代价之和。

  有多种算法可以构造最小生成树,其他多数都利用的最小生成的MST(minimum spanning tree)性质: 假设N={V, {E}}是一个连通网,U是顶点集V的一个非空子集。若(u,v)是一条具有最小权值(代价)的边,其中u属于U, v属于V-U,则必存在一棵包含边(u,v)的最小生成树。 该性质可以用反证法证明。

现介绍普里姆(Prim)算法是如何利用MST性质求连通图的最小生成树的:

假设N={V,{E}}是连通网,TE是N上最小生成树中边的集合。算法从U={u0} (u0属于V, TE={})开始,重复执行下述操作:在所有u属于U, v属于V-U 的边(u,v)属于E 中找一条代价最小的边(u0,v0)并入集合TE,同时v0并入U,直至U=V为止。

为实现这个算法需附设一个辅助数组closeedge, 以记录从U到V-U具有最小代价的边。对每个属于V-U的顶点vi ,在辅助数组中存在一个相应分量closedge[i-1],它包括两个域:

  1:lowcose:存储该边上的权; 显然closedge[i-1].lowcost = Min{cost(u,vi) | u属于U}

  2:vex: 存储该边依附的U中的顶点。

示意图

算法分析

  在代码实现中的MinSpanTree_PRIM函数中。若网中有n个顶点, 则第一个初始化的循环语句的频度为n,第二个循环语句的频度为n-1;其中第二个循环中有两个内循环:其一是在 closedge[v].lowcost中求最小值,其频度为n-1;其二是重新选择具有最小代价的边,其频度为n。由此,普里姆算法的时间复杂度为n^2, 与网中的边数无关,因此使用适用于求边稠密的网的最小生成树。

代码实现

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 #include <string.h>
  4
  5 #define DEBUG
  6
  7 #ifdef DEBUG
  8 #include <stdarg.h>
  9 #define LOG(args...) _log_(__FILE__, __FUNCTION__, __LINE__, ##args);
 10 void _log_(const char *file, const char *function, int line, const char * format, ...)
 11 {
 12     char buf[1024] = {0};
 13     va_list list;
 14     va_start(list, format);
 15     sprintf(buf, "[%s,%s,%d]", file, function, line);
 16     vsprintf(buf+strlen(buf), format, list);
 17     sprintf(buf+strlen(buf), "\n");
 18     va_end(list);
 19     printf(buf);
 20 }
 21 #else
 22 #define LOG
 23 #endif // DEBUG
 24
 25 #define INFINITY        100000    //最大值
 26 #define MAX_VERTEX_NUM    20        //最大顶点数
 27
 28 //---------邻接矩阵的存储结构-----------------------------------------
 29
 30 //////////////////////////////////////////////////////////////
 31 // 邻接矩阵作为图的存储结构
 32 //////////////////////////////////////////////////////////////
 33 typedef enum {DG, DN, UDG, UDN} GraphKind; //{有向图,有向网,无向图,无向网}
 34 typedef int  VRType;
 35 typedef char VertexType;    //顶点类型
 36 typedef struct{
 37     char note[10];
 38 }InfoType;
 39 typedef struct ArcCell{
 40     VRType adj;        //顶点关系类型:1)对无权图,用1或0表示相邻否;2)对带权图,则为权值类型
 41     InfoType *info;    //该弧相关信息的指针
 42 }ArcCell, AdjMatrix[MAX_VERTEX_NUM][MAX_VERTEX_NUM];
 43 typedef struct{
 44     VertexType vexs[MAX_VERTEX_NUM];    //顶点向量
 45     AdjMatrix arcs;        //邻接矩阵
 46     int vexnum, arcnum;    //图的当前顶点数和弧数
 47     GraphKind kind;        //图的种类标志
 48 }MGraph;
 49
 50
 51 //---------采用邻接矩阵创建无向网-----------------------------------------
 52
 53 //////////////////////////////////////////////////////////////
 54 // 若G中存在顶点u,则返回该顶点在图中位置;否则返回-1。
 55 //////////////////////////////////////////////////////////////
 56 int LocateVex(MGraph G, VertexType v){
 57     int i = 0;
 58     for(i=0; i<G.vexnum; i++){
 59         if(G.vexs[i] == v){
 60             return i;
 61         }
 62     }
 63     return -1;
 64 }
 65
 66
 67 //////////////////////////////////////////////////////////////
 68 // 采用数组表示法(邻接矩阵),构造无向网
 69 //////////////////////////////////////////////////////////////
 70 int CreateUDN(MGraph *G)
 71 {
 72     printf("\n创建一个无向网(带权):\n");
 73     int i = 0, j = 0, k = 0, IncInfo = 0;
 74     int v1 = 0, v2 = 0, w = 0;
 75     char tmp[10] = {0};
 76     printf("输入顶点数,弧数,其他信息标志位: ");
 77     scanf("%d,%d,%d", &G->vexnum, &G->arcnum, &IncInfo);
 78     for(i=0; i<G->vexnum; i++)
 79     {
 80         printf("输入第%d个顶点: ", i+1);
 81         memset(tmp, 0, sizeof(tmp));
 82         scanf("%s", tmp);
 83         G->vexs[i] = tmp[0];
 84     }
 85     for(i=0; i<G->vexnum; i++)
 86     {
 87         for(j=0; j<G->vexnum; j++)
 88         {
 89             G->arcs[i][j].adj = INFINITY;
 90             G->arcs[i][j].info = NULL;
 91         }
 92     }
 93     for(k=0; k<G->arcnum; k++)
 94     {
 95         printf("输入第%d条弧: 弧尾, 弧头,权值: ", k+1);
 96         memset(tmp, 0, sizeof(tmp));
 97         scanf("%s", tmp);
 98         sscanf(tmp, "%c,%c,%d", &v1, &v2, &w);
 99         i = LocateVex(*G, v1);
100         j = LocateVex(*G, v2);
101         G->arcs[i][j].adj = w;
102         if(IncInfo){
103             //
104         }
105         G->arcs[j][i] = G->arcs[i][j];
106     }
107     return 0;
108 }
109
110 int CreateGraph(MGraph *G)
111 {
112     printf("输入图类型: -有向图(0), -有向网(1), -无向图(2), +无向网(3): ");
113     scanf("%d", &G->kind);
114     switch(G->kind)
115     {
116         case DG:
117         case DN:
118         case UDG:
119             printf("还不支持!\n");
120             return -1;
121         case UDN:
122             return CreateUDN(G);
123         default:
124             return -1;
125     }
126 }
127
128
129 //////////////////////////////////////////////////////////////
130 // 打印邻接矩阵中的信息
131 //////////////////////////////////////////////////////////////
132 void printG(MGraph G)
133 {
134     printf("\n打印邻接矩阵:\n");
135     if(G.kind == DG){
136         printf("类型:有向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
137     }else if(G.kind == DN){
138         printf("类型:有向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
139     }else if(G.kind == UDG){
140         printf("类型:无向图;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
141     }else if(G.kind == UDN){
142         printf("类型:无向网;顶点数 %d, 弧数 %d\n", G.vexnum, G.arcnum);
143     }
144     int i = 0, j = 0;
145     printf("\t");
146     for(i=0; i<G.vexnum; i++)
147         printf("%c\t", G.vexs[i]);
148     printf("\n");
149     for(i=0; i<G.vexnum; i++){
150         printf("%c\t", G.vexs[i]);
151         for(j=0; j<G.vexnum; j++){
152             if(G.arcs[i][j].adj == INFINITY){
153                 printf("INF\t");
154             }else{
155                 printf("%d\t", G.arcs[i][j].adj);
156             }
157         }
158         printf("\n");
159     }
160 }
161
162
163 //---------求最小生成树的算法。(普里姆算法)-----------------------------------------
164
165 //////////////////////////////////////////////////////////////
166 // 定义辅助数组CloseEdge: 记录从顶点集U到V-U的代价最小的边的辅助数组。
167 //////////////////////////////////////////////////////////////
168 struct Edge{
169     VertexType adjvex;
170     VRType  lowcost;
171 }CloseEdge[MAX_VERTEX_NUM];
172
173 //////////////////////////////////////////////////////////////
174 // 返回辅助数组中, 权值最小的顶点的位置。
175 //////////////////////////////////////////////////////////////
176 int minimum(struct Edge edgelist[], int count)
177 {
178     int ret = -1;
179     int min = INFINITY;
180     int i = 0;
181     for(i=0; i<count; i++) {
182         if ((edgelist[i].lowcost) && (edgelist[i].lowcost < min)) {
183             ret = i;
184             min = edgelist[i].lowcost;
185         }
186     }
187     return ret;
188 }
189
190 //////////////////////////////////////////////////////////////
191 // 采用普里姆算法求最小生成树的算法。
192 //////////////////////////////////////////////////////////////
193 void MinSpanTree_PRIM(MGraph G, VertexType v)
194 {
195     printf("\n采用普里姆算法求邻接矩阵存储的带权的无向网的最小生成树,所求最小生成树的边依次为:\n");
196     int k = -1;
197     int i = 0;
198     int j = 0;
199     k = LocateVex(G, v);
200     //辅助数组初始化
201     for(j=0; j<G.vexnum; j++){
202         if(j!=k)
203         {
204             CloseEdge[j].adjvex = v;
205             CloseEdge[j].lowcost = G.arcs[k][j].adj;
206         }
207     }
208     //初始状态下, U={v}
209     CloseEdge[k].lowcost = 0;
210     //选择其余的G.vexnum-1个顶点
211     for(i=1; i<G.vexnum; i++)
212     {
213         //求出T的下一个结点,第k个顶点
214         k = minimum(CloseEdge, G.vexnum);
215         //输出生成树的边
216         printf("%c, %c\n", CloseEdge[k].adjvex, G.vexs[k]);
217         //第k个顶点并入U集合
218         CloseEdge[k].lowcost = 0;
219         //新顶点并入U后重新选择最小边。
220         for(j=0; j<G.vexnum; j++){
221             if(G.arcs[k][j].adj < CloseEdge[j].lowcost){
222                 CloseEdge[j].adjvex = G.vexs[k];
223                 CloseEdge[j].lowcost = G.arcs[k][j].adj;
224             }
225         }
226     }
227 }
228
229 int main(int argc, char *argv[])
230 {
231     MGraph G;
232     if(CreateGraph(&G) > -1)
233         printG(G);
234     MinSpanTree_PRIM(G, G.vexs[0]);
235     return 0;
236 }

最小生成树(普里姆算法)

代码运行

/home/lady/CLionProjects/untitled/cmake-build-debug/untitled
输入图类型: -有向图(0), -有向网(1), -无向图(2), +无向网(3): 3

创建一个无向网(带权):
输入顶点数,弧数,其他信息标志位: 6,10,0
输入第1个顶点: a
输入第2个顶点: b
输入第3个顶点: c
输入第4个顶点: d
输入第5个顶点: e
输入第6个顶点: f
输入第1条弧: 弧尾, 弧头,权值: c,a,1
输入第2条弧: 弧尾, 弧头,权值: c,b,5
输入第3条弧: 弧尾, 弧头,权值: c,d,5
输入第4条弧: 弧尾, 弧头,权值: c,e,6
输入第5条弧: 弧尾, 弧头,权值: c,f,4
输入第6条弧: 弧尾, 弧头,权值: a,b,6
输入第7条弧: 弧尾, 弧头,权值: b,e,3
输入第8条弧: 弧尾, 弧头,权值: e,f,6
输入第9条弧: 弧尾, 弧头,权值: f,d,2
输入第10条弧: 弧尾, 弧头,权值: d,a,5

打印邻接矩阵:
类型:无向网;顶点数 6, 弧数 10
    a    b    c    d    e    f
a    INF    6    1    5    INF    INF
b    6    INF    5    INF    3    INF
c    1    5    INF    5    6    4
d    5    INF    5    INF    INF    2
e    INF    3    6    INF    INF    6
f    INF    INF    4    2    6    INF    

采用普里姆算法求邻接矩阵存储的带权的无向网的最小生成树,所求最小生成树的边依次为:
a, c
c, f
f, d
c, b
b, e

Process finished with exit code 0

原文地址:https://www.cnblogs.com/aimmiao/p/10092174.html

时间: 2025-01-07 08:18:55

图->连通性->最小生成树(普里姆算法)的相关文章

数据结构之最小生成树(普里姆算法)

1)普里姆算法 可取图中任意一个顶点v作为生成树的根,之后若要往生成树上添加顶点w,则在顶点v和顶点w之间必定存在一条边,并且 该边的权值在所有连通顶点v和w之间的边中取值最小.一般情况下,假设n个顶点分成两个集合:U(包含已落在生成树上 的结点)和V-U(尚未落在生成树上的顶点),则在所有连通U中顶点和V-U中顶点的边中选取权值最小的边. 例如:起始生成树上面就一个顶点.为了连通两个集合,在可选的边中,选择权值最小的.需要辅助数组,V-U中所有顶点. 具体实例如下图所示:求下图的最小生成树 我

数据结构-最小生成树-普里姆算法

转自https://blog.csdn.net/ZGUIZ/article/details/54633115 首先仍然是预定义: 1 #define OK 1 2 #define ERROR 0 3 #define Max_Int 32767 4 #define MVNum 100 5 6 typedef int Status; 7 typedef char VerTexType; 8 typedef int ArcType; 9 10 struct{ 11 VerTexType adjvex;

数据结构(五)图---最小生成树(普里姆算法)

一:最小生成树 (一)定义 我们把构造连通网的最小代价生成树称为最小生成树 (二)什么是最小生成树? 1.是一棵树 1)无回路 2)N个顶点,一定有N-1条边 2.是生成树 1)包含全部顶点 2)N-1条边都在图中 3.边的权重和最小 (三)案例说明 在实际生活中,我们常常碰到类似这种一类问题:如果要在n个城市之间建立通信联络网, 则连通n个城市仅仅须要n-1条线路.这时.我们须要考虑这样一个问题.怎样在最节省经费前提 下建立这个通信网.换句话说,我们须要在这n个城市中找出一个包括全部城市的连通

ACM第四站————最小生成树(普里姆算法)

对于一个带权的无向连通图,其每个生成树所有边上的权值之和可能不同,我们把所有边上权值之和最小的生成树称为图的最小生成树. 普里姆算法是以其中某一顶点为起点,逐步寻找各个顶点上最小权值的边来构建最小生成树. 其中运用到了回溯,贪心的思想. 废话少说吧,这个其实是一个模板,直接套用就好!直接上题吧!这些东西多练就好! 一.最小生成树: 题目描述 求一个连通无向图的最小生成树的代价(图边权值为正整数). 输入 第 一行是一个整数N(1<=N<=20),表示有多少个图需要计算.以下有N个图,第i图的第

数据结构例程——最小生成树的普里姆算法

本文是[数据结构基础系列(7):图]中第11课时[最小生成树的普里姆算法]的例程. (程序中graph.h是图存储结构的"算法库"中的头文件,详情请单击链接-) #include <stdio.h> #include <malloc.h> #include "graph.h" void Prim(MGraph g,int v) { int lowcost[MAXV]; //顶点i是否在U中 int min; int closest[MAXV]

46. 蛤蟆的数据结构笔记之四十六普里姆算法

46. 蛤蟆的数据结构笔记之四十六普里姆算法 本篇名言:"手莫伸 ,伸手必被捉.党与人民在监督 ,万目睽睽难逃脱.汝言惧捉手不伸 ,他道不伸能自觉 , 其实想伸不敢伸 ,人民咫尺手自缩.-- 陈毅" 连通图的生成树是一个极小的连通子图,它含有图中全部的顶点,但只有足以构成一棵树的n-1条边.所谓的最小成本,就是n个顶点,用n-1条边把一个连通图连接起来,并且使得权值的和最小.构造连通网的最小代价生成树,即最小生成树(Minimum Cost Spanning Tree). 找连通图的最

普里姆算法-prim

算法代码: C++ Code 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 /* Prim算法生成最小生成树  */ void MiniSpanTree_Prim(MGraph MG) { int min, i, j, k; int adjvex[MAXVEX];/* 

普里姆算法介绍

普里姆(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中包含了最小生成树中的所有边. 普里姆算法图解 以上图G4为例,

普里姆算法,克鲁斯卡尔算法,迪杰斯特拉算法,弗洛里德算法

做数据结构的课程设计顺便总结一下这四大算法,本人小白学生一枚, 如果总结的有什么错误,希望能够告知指正 普里姆算法如图所示prim 找出最短的边,再以这条边构成的整体去寻找与之相邻的边,直至连接所有顶点,生成最小生成树,时间复杂度为O(n2) 克鲁斯卡尔算法如图所示kruskal 克鲁斯卡尔算法,假设连通网N=(N,{E}),则令最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点 自成一个连通分量.在E中选择代价最小的边,若该边依附的定顶点落在T中不同的连通分量上,