zoukankan      html  css  js  c++  java
  • 图->连通性->最小生成树(普里姆算法)

    文字描述

      用连通网来表示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), "
    ");
     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("
    创建一个无向网(带权):
    ");
     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("还不支持!
    ");
    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("
    打印邻接矩阵:
    ");
    135     if(G.kind == DG){
    136         printf("类型:有向图;顶点数 %d, 弧数 %d
    ", G.vexnum, G.arcnum);
    137     }else if(G.kind == DN){
    138         printf("类型:有向网;顶点数 %d, 弧数 %d
    ", G.vexnum, G.arcnum);
    139     }else if(G.kind == UDG){
    140         printf("类型:无向图;顶点数 %d, 弧数 %d
    ", G.vexnum, G.arcnum);
    141     }else if(G.kind == UDN){
    142         printf("类型:无向网;顶点数 %d, 弧数 %d
    ", G.vexnum, G.arcnum);
    143     }
    144     int i = 0, j = 0;
    145     printf("	");
    146     for(i=0; i<G.vexnum; i++)
    147         printf("%c	", G.vexs[i]);
    148     printf("
    ");
    149     for(i=0; i<G.vexnum; i++){
    150         printf("%c	", G.vexs[i]);
    151         for(j=0; j<G.vexnum; j++){
    152             if(G.arcs[i][j].adj == INFINITY){
    153                 printf("INF	");
    154             }else{
    155                 printf("%d	", G.arcs[i][j].adj);
    156             }
    157         }
    158         printf("
    ");
    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("
    采用普里姆算法求邻接矩阵存储的带权的无向网的最小生成树,所求最小生成树的边依次为:
    ");
    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
    ", 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
  • 相关阅读:
    虚拟机更新为阿里数据源
    linux设置开机同步时间
    linux免密码登录
    Google 开源的 Python 命令行库:深入 fire(一)
    统计各个分类和标签下的文章数
    缩写名词解释
    Google 开源的 Python 命令行库:初探 fire
    稳定易用的 Django 分页库,完善分页功能
    Python 命令行之旅:使用 click 实现 git 命令
    Python 库打包分发、setup.py 编写、混合 C 扩展打包的简易指南(转载)
  • 原文地址:https://www.cnblogs.com/aimmiao/p/10092174.html
Copyright © 2011-2022 走看看