数据结构之最小生成树(克鲁斯卡尔算法)

1)克鲁斯卡尔算法

普里姆算法是以某顶点为起点,逐步找各顶点上最小权值的边来构建最小生成树。

克鲁斯卡尔算法是直接以边为目标去构建。

因为权值是在边上,直接去找最小权值的边来构建生成树也是很自然的想法,只不过构建时要考虑是否会形成环路而已。

此时我们用到了图的存储结构中的边集数组结构。

以下是边集数组结构的定义代码:

1 typedef struct
2 {
3     int begin;                         //边的起点的下标
4     int end;                           //边的终点的下标
5     int weight;                        //边的权值
6 }Edge;

本算法所用同普里姆算法的实例,我们直接创建图的边集数组。

并对边的权值从小到大排序后如下图:

重点分析:

克鲁斯卡尔算法主要针对边展开,边数少时效率会很高,所以对于稀疏图有优势

而普利姆算法对于稠密图,即边数非常多的情况会好些。

代码实现如下:

  1 #include "stdafx.h"
  2 #include<iostream>
  3 #include<string>
  4 using namespace std;
  5 typedef struct MGraph
  6 {
  7     string vexs[60];                   //存放顶点
  8     int arcs[100][100];                //存放邻接矩阵矩阵
  9     int vexnum, arcum;                 //总顶点数以及总边数
 10 }MGraph;
 11 typedef struct
 12 {
 13     int begin;                         //边的起点的下标
 14     int end;                           //边的终点的下标
 15     int weight;                        //边的权值
 16 }Edge;
 17 
 18 int locateVex(MGraph G, string u)      //找到则返回i,否则返回-1
 19 {
 20     for (int i = 0; i < G.vexnum; i++)
 21         if (G.vexs[i] == u)
 22             return i;
 23     return -1;
 24 }
 25 
 26 void CreateGraphUDG(MGraph &G)         //构造无向图
 27 {
 28     string v1, v2;                     //两个顶点信息
 29     int w;                             //两顶点间边的权值
 30     int i, j, k;
 31     cout << "请输入顶点数和边数:";
 32     cin >> G.vexnum >> G.arcum;
 33 
 34     cout << "请输入顶点:";
 35     for (i = 0; i < G.vexnum; i++)
 36         cin >> G.vexs[i];
 37 
 38     for (i = 0; i < G.vexnum; i++)     //先将邻接矩阵中所有元素都设为无穷大,这里默认10000为无穷大
 39         for (j = 0; j < G.vexnum; j++)
 40             G.arcs[i][j] = 10000;
 41 
 42     cout << "请输入边和权值:" << endl;
 43     for (k = 0; k < G.arcum; k++)
 44     {
 45         cin >> v1 >> v2 >> w;
 46         i = locateVex(G, v1);
 47         j = locateVex(G, v2);
 48         G.arcs[i][j] = G.arcs[j][i] = w;//关于主对角线对称的元素是相等的
 49     }
 50 
 51 }
 52 
 53 int cmp(const void* a, const void* b) 
 54 {
 55     return (*(Edge*)a).weight - (*(Edge*)b).weight;
 56 }
 57 
 58 int Find(int *parent, int f)           //查找连接顶点的尾部下标  
 59 {
 60     while (parent[f] > 0) 
 61         f = parent[f];
 62     return f;
 63 }
 64 
 65 void MiniSpanTree_Kruskal(MGraph G) 
 66 {
 67     int i, j, n, m;
 68     int k = 0;
 69     int parent[100];                   //用于寻找根节点的数组   
 70     Edge edges[100];                   //定义边集数组,edge的结构为begin,end,weight,均为整型  
 71     for (i = 0; i < G.vexnum - 1; i++) // 用来构建边集数组并排序(将邻接矩阵的对角线右边的部分存入边集数组中)  
 72     {
 73         for (j = i + 1; j < G.vexnum; j++) 
 74         {
 75             if (G.arcs[i][j] < 10000) 
 76             {
 77                 edges[k].begin = i;    //编号较小的结点为首 
 78                 edges[k].end = j;      //编号较大的结点为尾  
 79                 edges[k].weight = G.arcs[i][j];
 80                 k++;
 81             }
 82         }
 83     }
 84     qsort(edges, G.arcum, sizeof(Edge), cmp);//为边集数组Edge排序  
 85 
 86     for (i = 0; i < G.vexnum; i++)
 87         parent[i] = 0;
 88 
 89     cout << "打印最小生成树:" << endl;
 90     for (i = 0; i < G.arcum; i++) 
 91     {
 92         n = Find(parent, edges[i].begin); //寻找边edge[i]的“首节点”所在树的树根  
 93         m = Find(parent, edges[i].end);   //寻找边edge[i]的“尾节点”所在树的树根
 94         if (n != m)                       //假如n与m不等,说明两个顶点不在一棵树内,因此这条边的加入不会使已经选择的边集产生回路
 95         {
 96             parent[n] = m;                //将此边的结尾顶点放入下标为起点的parent中,表示此顶点已经在生成树集合中
 97             cout << G.vexs[edges[i].begin] << "-" <<G.vexs[ edges[i].end] << " " << edges[i].weight << endl;
 98         }
 99     }
100 }
101 
102 int main()
103 {
104     MGraph G;
105     CreateGraphUDG(G);
106     MiniSpanTree_Kruskal(G);
107     cout << endl;
108     return 0;
109 }

输出结果:

猜你喜欢

转载自www.cnblogs.com/Trojan00/p/8986874.html