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


    【1】什么是最小生成树?

    对于连通的带权图(连通网)G,其生成树也是带权的。

    生成树T各边的权值总和称为该树的权。

    权最小的生成树称为G的最小生成树(Minimum SpannirngTree)。简记为MST。

    注意:最小是指权值最小

    一个连通图的生成树是一个极小的连通子图,它包含全部的顶点,但只有足以构成一棵树的n-1条边。

    求最小生成树有两种算法:普里姆算法和克鲁斯卡尔算法

    不好理解?看不懂?能通俗点不?看个实例哈:

    假设你是电信实施工程师,需要为一个镇的九个村庄架设通信网络做设计。

    村庄位置大致如下图,之间连线的数字表示村与村间的可通达直线距离。

    你们领导要求你必须用最小的成本完成这次任务。你说怎么办?

    好,这就是很现实的一个最小生成树案例。且听下面详解。

    【2】普里姆算法

    利用 普里姆算法 要解决如上问题,首先我们构造图的邻接矩阵。如下图所示:

    注意:实际中我们用65535来代表无穷大。

    关于普里姆算法以及讲解如下图

    针对上面我们遇到的实际案例,普里姆算法执行循环过程如下图

    每次所选最小边分别如 图1-图8 所示

    最后用所有边把各个顶点连通也就是所谓的最小生成树。

    【3】普里姆算法的实现

    实现代码如下:

      1 #include <iostream>
      2 #include "SeqList.h"
      3 #include <iomanip>
      4 using namespace std;
      5 
      6 #define  INFINITY  65535
      7 
      8 template<class NameType, class DistType>
      9 class Graph
     10 {
     11 private:
     12     SeqList<NameType> Vertices;
     13     DistType **Edges;
     14     int nVer, nEdges;
     15 
     16 public:
     17     Graph() 
     18         : Edges(NULL)
     19         , nEdges(0)
     20         , nVer(0)
     21     {}
     22     ~Graph()
     23     {}
     24 
     25 public:
     26 
     27     istream & operator>>(istream &in)
     28     {
     29         int v, u, value;
     30         int i, j;
     31         NameType item;
     32         cout << "请输入顶点的个数: " << endl;
     33         in >> nVer;
     34         cout << "请输入顶点的数据信息: " << endl;
     35         for (i = 0; i < nVer; ++i)
     36         {
     37             in >> item;
     38             Vertices.push_back(item);    // 保存全部顶点
     39         }
     40         /////二维数组的创建并初始化
     41         Edges = new DistType*[nVer]; // DistType *ar[10];
     42         for (i = 0; i < nVer; ++i)
     43         {
     44             Edges[i] = new DistType[nVer];
     45             for (j = 0; j < nVer; ++j)
     46             {
     47                 Edges[i][j] = 0;
     48             }
     49         }
     50         cout << "请输入边的个数: " << endl;
     51         in >> nEdges;
     52         cout << "请输入边的信息:" << endl;
     53         for (i = 0; i < nEdges; ++i)
     54         {
     55             in >> v >> u >> value;
     56             Edges[v][u] = value;
     57             Edges[u][v] = value;
     58         }
     59         return in;
     60     }
     61     ostream & operator<<(ostream &out) const
     62     {
     63         int i, j;
     64         out << "顶点信息 " << endl;
     65         for (i = 1; i <= nVer; ++i)
     66         {
     67             out << Vertices[i] << setw(5);
     68         }
     69         out << endl;
     70         out << "矩阵信息:" << endl;
     71         out << setw(10);
     72         for (i = 1; i <= nVer; ++i)
     73         {
     74             out << Vertices[i] << setw(5);
     75         }
     76         out << endl;
     77         for (i = 0; i < nVer; ++i)
     78         {
     79             out << Vertices[i+1] << setw(5);
     80             for (j = 0; j < nVer; ++j)
     81             {
     82                 if (0 == Edges[i][j] && i != j)
     83                     Edges[i][j] = INFINITY;
     84                 cout << Edges[i][j] << setw(5);
     85             }
     86             out << endl;
     87         }
     88         out << endl;
     89 
     90         return out;
     91     }
     92     //  图采用邻接矩阵存储  最小生成树 普里姆算法
     93     void MiniSpanTree()  
     94     {  
     95         int min = 0, i = 0, j = 0, k = 0;
     96         int* adjvex = new  int[nVer];    // 保存相关顶点下标  
     97         int* lowcost = new int[nVer];    // 保存相关顶点间边的权值  
     98         lowcost[0] = 0;         // 初始化第一个权值为0,即V0已加入生成树
     99         //lowcost的值为0,在这里就是此下标的顶点已经加入生成树    
    100         adjvex[0] = 0;          // 初始化第一个顶点下标为0  
    101 
    102         for (i = 1; i < nVer; ++i)  //循环除过下标为0外的全部顶点
    103         {  
    104             lowcost[i] = Edges[0][i];  // 将v0顶点与之有边的权值存入数组 
    105             adjvex[i] = 0;             // 并初始化都为v0的下标
    106         }  
    107 
    108         for (i = 1; i < nVer; ++i) 
    109         {  
    110             min = INFINITY;  // 初始化最小权值为常数,通常设置为不可能到达的数值
    111             k = 0;    // 复位
    112 
    113             for (j = 1; j < nVer; ++j)  
    114             {  
    115                 // 如果两个顶点之间存在边有权值,不为0并且小于min  
    116                 if (lowcost[j] != 0 && lowcost[j] < min)  
    117                 {  
    118                     min = lowcost[j];  // 缓存最小值
    119                     k = j;    // 将当前最小值的下标缓存入k
    120                 }
    121             }  
    122             cout << "("<< adjvex[k] << "," << k << ")" << endl;   //打印当前顶点边中权值最小的边  
    123             lowcost[k] = 0;                     // 将当前顶点的权值设为0,表示此顶点已经完成任务  
    124 
    125             for (j = 1; j < nVer; ++j)  // 循环所有节点
    126             {  
    127                 if (lowcost[j] != 0 && Edges[k][j] < lowcost[j])  
    128                 {  // 若下标为k顶点各边权值小于此前这些顶点未被加入生成树权值
    129                     lowcost[j] = Edges[k][j]; // 用较小者替换  
    130                     adjvex[j] = k;  // 将下标为k的顶点存入adjvex
    131                 }  
    132             }  
    133         }
    134 
    135         delete []adjvex;
    136         delete []lowcost;
    137         adjvex = NULL;
    138         lowcost = NULL;
    139     }
    140 };
    141 
    142 template<class NameType, class DistType>
    143 istream & operator>>(istream &in, Graph<NameType,DistType> &g)
    144 {
    145     g >> in;
    146     return in;
    147 }
    148 
    149 template<class NameType, class DistType>
    150 ostream & operator<<(ostream &out, const Graph<NameType,DistType> &g)
    151 {
    152     g << out;
    153     return out;
    154 }
    155 
    156 void main()
    157 {
    158     Graph<char, int> myg;
    159     cin >> myg;
    160     cout << "打印所有输入信息:" << endl;
    161     cout << myg << endl;
    162     cout << "最小生成树边信息如下:" << endl;
    163     myg.MiniSpanTree();
    164     cout << endl;
    165 }
    View Code

    代码中所引用是头文件SeqList.h从随笔《顺序表》拷贝即可,也可以自己另行处理。

    Good  Good  Study,  Day  Day  Up.

    顺序   选择   循环   总结 

  • 相关阅读:
    C# NameValueCollection
    visual studio使用技巧创建自己代码片段
    C#在DataTable中使用LINQ
    [转]C# 中的.pdb/ .vshost.exe/ .vshost.exe.manifest文件讨论
    C#自定义控件在添加引用后不显示在工具箱的解决方法
    Java 工程师
    redis-CRC16
    sql server-当天日期减去一天 应该如何写
    清除访问Windows共享时缓存的凭据
    cmd下查看当前登陆用户
  • 原文地址:https://www.cnblogs.com/Braveliu/p/3457262.html
Copyright © 2020-2023  润新知