zoukankan      html  css  js  c++  java
  • 最小生成树-Prim算法与Kruskal算法

    一、最小生成树(MST)

      ①、生成树的代价:设G=(V,E)是一个无向连通网,生成树上各边的权值之和称为该生成树的代价。 

      ②、最小生成树:在图G所有生成树中,代价最小的生成树称为最小生成树。

      最小生成树的概念可以应用到许多实际问题中。 例:在n个城市之间建造通信网络,至少要架设n-1条通信线路,而每两个城市之间架设通信线路的造价是不一样的,那么如何设计才能使得总造价最小?

      ③、MST性质:假设G=(V, E)是一个无向连通网,U是顶点集V的一个非空子集。若(u, v)是一条具有最小权值的边,其中u∈U,v∈V-U,则必存在一棵包含边(u, v)的最小生成树。

    二、Prim算法

      ①基本思想:设G=(V, E)是具有n个顶点的连通网,T=(U, TE)是G的最小生成树, T的初始状态为U={u0}(u0∈V),TE={ },重复执行下述操作:在所有u∈U,v∈V-U的边中找一条代价最小的边(u, v)并入集合TE,同时v并入U,直至U=V。

      关键:是如何找到连接U和V-U的最短边,利用MST性质,可以用下述方法构造候选最短边集:对应V-U中的每个顶点,保留从该顶点到U中的各顶点的最短边。

      ②数据结构设计

      数组lowcost[n]:用来保存集合V-U中各顶点与集合U中顶点最短边的权值,lowcost[v]=0表示顶点v已加入最小生成树中;

      数组adjvex[n]:用来保存依附于该边(集合V-U中各顶点与集合U中顶点的最短边)在集合U中的顶点。

      如何用数组lowcost和adjvex表示候选最短边集?

      lowcost[i]=w;表示顶点vi和顶点vk之间的权值为w,

      adjvex[i]=k;其中:vi∈ V-U 且vk ∈U。

      ③Prim算法——伪代码

    1. 初始化两个辅助数组lowcost和adjvex;
    2. 输出顶点u0,将顶点u0加入集合U中;
    3. 重复执行下列操作n-1次
       3.1 在lowcost中选取最短边,取adjvex中对应的顶点序号k;
       3.2 输出顶点k和对应的权值;
       3.3 将顶点k加入集合U中;
       3.4 调整数组lowcost和adjvex;

      ④C++实现

     1 #include<iostream>
     2 #include<fstream>
     3 using  namespace std;
     4 
     5 #define MAX 100
     6 #define MAXCOST 0x7fffffff
     7 
     8 int graph[MAX][MAX];
     9 
    10 /*方法二*/
    11 struct Node
    12 {
    13     int lowcost;// 权值
    14     int adjvex;// 候选最短边的邻接点
    15 };
    16 Node shortEdge[MAX];// 候选最短边集合
    17 void prim2(int graph[][MAX], int n)
    18 {    
    19      // 初始化辅助数组shortEdge,所有点到点1的权值
    20     for (int i = 2; i <= n; i++)
    21     {
    22         shortEdge[i].lowcost = graph[1][i];
    23         shortEdge[i].adjvex = 1;
    24     }
    25     shortEdge[1].lowcost = 0;// 将顶点1加入集合U
    26     for (int i = 2; i <= n; i++)
    27     {
    28         // 寻找最短边的邻接点k
    29         int k = 0, min = MAXCOST;
    30         for (int j = 2; j <= n; j++)
    31         {
    32             if (min >shortEdge[j].lowcost&&shortEdge[j].lowcost!=0)
    33             {
    34                 min = shortEdge[j].lowcost;
    35                 k = j;
    36             }
    37         }
    38         cout << "(" << k << shortEdge[k].adjvex << ")" << shortEdge[k].lowcost;
    39         shortEdge[k].lowcost = 0;// 将顶点k加入结合U中
    40         for (int j = 2; j <= n; j++)
    41         {
    42             if (graph[k][j] < shortEdge[j].lowcost)
    43             {
    44                 shortEdge[j].lowcost = graph[k][j];
    45                 shortEdge[j].adjvex = k;
    46             }
    47         }
    48     }
    49 }
    50 
    51 int main()
    52 {
    53     int i, j, k, m, n;
    54     int x, y, cost;
    55     ifstream in("input.txt");
    56     in >> m >> n;//m=顶点的个数,n=边的个数
    57     //初始化图G
    58     for (i = 1; i <= m; i++)
    59     {
    60         for (j = 1; j <= m; j++)
    61         {
    62             graph[i][j] = MAXCOST;
    63         }
    64     }
    65     //构建图G
    66     for (k = 1; k <= n; k++)
    67     {
    68         in >> i >> j >> cost;
    69         graph[i][j] = cost;
    70         graph[j][i] = cost;
    71     }    
    72     prim2(graph, m);
    73     system("pause");
    74     return 0;
    75 }

    三、Kruskal算法

      ①基本思想:设无向连通网为G=(V, E),令G的最小生成树为T=(U, TE),其初态为U=V,TE={ },然后,按照边的权值由小到大的顺序,考察G的边集E中的各条边。若被考察的边的两个顶点属于T的两个不同的连通分量,则将此边作为最小生成树的边加入到T中,同时把两个连通分量连接为一个连通分量;若被考察边的两个顶点属于同一个连通分量,则舍去此边,以免造成回路,如此下去,当T中的连通分量个数为1时,此连通分量便为G的一棵最小生成树。

      ②数据结构设计:因为Kruskal算法是依次对图中的边进行操作,因此考虑用边集数组存储图中的边,为了提高查找最短边的速度,可以先对边集数组按边上的权值排序。

     1 struct EdgeType
     2 {
     3     int from, to;// 边依附的两个顶点
     4     int weight;// 边的权值
     5 };
     6 
     7 template<class T>
     8 struct EdgeGraph
     9 {
    10     T vertex[Maxvertex];// 存放顶点信息    
    11     vector<EdgeType> edge;// 存放边的数组(用vector便于排序)
    12     int vertexNum, edgeNum;//顶点数和边数
    13 };

      ③Kruskal算法——伪代码

    1. 初始化:U=V;  TE={ }; 
    2. 循环直到T中的连通分量个数为1  
         2.1 在E中寻找最短边(u,v);
         2.2 如果顶点u、v位于T的两个不同连通分量,则
               2.2.1 将边(u,v)并入TE;
               2.2.2 将这两个连通分量合为一个;
         2.3 在E中标记边(u,v),使得(u,v)不参加后续最短边的选取;

      ④C++实现

    #include<iostream>
    #include<fstream>
    #include<vector>
    #include<algorithm>
    using  namespace std;
    
    const int Maxvertex = 10;// 最多顶点数
    const int MaxEdge = 100;// 最多边数
    
    struct EdgeType
    {
        int from, to;// 边依附的两个顶点
        int weight;// 边的权值
    };
    
    template<class T>
    struct EdgeGraph
    {
        T vertex[Maxvertex];// 存放顶点信息    
        vector<EdgeType> edge;// 存放边的数组(用vector便于排序)
        int vertexNum, edgeNum;//顶点数和边数
    };
    int FindRoot(int parent[], int v)// 求顶点的双亲结点
    {
        int t = v;
        while(parent[t]> -1)
            t = parent[t];    
        return t;
    }
    void Kruskal(EdgeGraph<int> G)
    {
        int parent[Maxvertex];
        for (int i = 0; i < G.vertexNum; i++)
            parent[i] = -1;// 表示顶点i没有双亲结点
        for (int num = 0, i = 0; i < G.edgeNum; i++)
        {
            int vex1 = FindRoot(parent, G.edge[i].from);
            int vex2 = FindRoot(parent, G.edge[i].to);
            if (vex1 != vex2)
            {
                cout << "(" << G.edge[i].from << G.edge[i].to << ")" << endl;
                parent[vex2] = vex1;// 合并生成树
                num++;
                if (num == G.vertexNum - 1)
                    return;
            }
        }
    
    }
    
    bool sort_by_weight(EdgeType&k1, EdgeType&k2)
    {
        return k1.weight < k2.weight;
    }
    int main()
    {
        EdgeGraph<int> Edgraph;// 存放图的信息
        ifstream in("input.txt");
        in >> Edgraph.vertexNum >> Edgraph.edgeNum;
        //构建图G
        for (int k = 0; k <Edgraph.edgeNum; k++)
        {
            EdgeType temp;
            in >> temp.from >> temp.to >> temp.weight;
            Edgraph.edge.push_back(temp);
        }
        // 将边按照权值排序
        sort(Edgraph.edge.begin(), Edgraph.edge.end(), sort_by_weight);
        Kruskal(Edgraph);
        system("pause");
        return 0;
    }
    input.txt
    6 10
    1 2 6
    1 3 1
    1 4 5
    2 3 5
    2 5 3
    3 4 5
    3 5 6
    3 6 4
    4 6 2
    5 6 6
  • 相关阅读:
    pyinstaller
    screen
    docker
    rsync
    shutil模块
    mysql innodb 理解
    B 树和B+树存储的区别
    B-树原理分析
    mysql 通过mycat 读写分离
    mysql 主从复制
  • 原文地址:https://www.cnblogs.com/smile233/p/8287576.html
Copyright © 2011-2022 走看看