最小生成树

2022-09-02  本文已影响0人  lxr_

生成树:

无向图的生成树:

最小生成树:

普里姆算法(Prim)算法:

(1)设N=(V,E)是连通网,TE是N上最小生成树中边的集合;
(2)初始令U={u0},(u0属于V),TE={};
(3)在所有u属于U,v属于V-U的边(u,v)属于E中,找一条代价最小的边(u0,v0);
(4)将(u0,v0)并入集合TE,同时v0并入U;
(5)重复上述操作直至U=V为止,则T=(V,TE)为N的最小生成树

克鲁斯卡尔(Kruskal)算法:

(1)设连通网N=(V,E),令最小生成树初始状态为只有n个顶点而无边的非连通图T=(V,{}),每个顶点自成一个连通分量。
(2)在E中选取代价最小的边,若该边依附的顶点落在T中不同的连通分量上(即:不能形成环),则将此边加入到T中;否则,舍去此边,选取下一条代价最小的边。
(3)依次类推,直至T中所有顶点都在同一连通分量上为止。

两种算法对比:

算法对比
代码示例
关于图的邻接矩阵结构创建可查看以前的文章https://www.jianshu.com/p/b50ba1b3c327
MGraph.h
//Prim算法生成最小生成树
void MiniSpanTree_Prim(MGraph G);

//克鲁斯卡尔算法
struct Edge                     //定义边集数组结构
{
    int begin;                  //边的起点
    int end;                    //边的终点
    int weight;                 //边的权重

    Edge operator=(const Edge& e)
    {
        begin = e.begin;
        end = e.end;
        weight = e.weight;

        return *this;
    }
};

//将邻接矩阵转换为边集数组并按照权值升序排序
void MGraph2Edge(MGraph G, Edge edges[]);

//Kruskal算法生成最小生成树
void MiniSpanTree_Kruskal(MGraph G);

//查找连线顶点的尾部下标
int Find(int* parent, int f);

MGraph.cpp

//Prim算法生成最小生成树
void MiniSpanTree_Prim(MGraph G)
{
    //step1:初始化
    int adjvex[MAXVEX];                                 //保存最小生成树中边的一端顶点(其实是已在生成树中的顶点)
    int lowcost[MAXVEX];                                //保存相关顶点间边的权值

    lowcost[0] = 0;                                     //初始化第一个权值为0,即v0加入生成树,通过此数组记录已加入生成树的顶点与其它顶点之间的边的最小值
    adjvex[0] = 0;                                      //初始化第一个顶点下标为0,通过此数组记录已加入生成树的顶点

    for (int i = 1; i < G.numVertexes; i++)             //遍历除了v0顶点的其它顶点
    {
        lowcost[i] = G.arc[0][i];                       //记录顶点v0与其它边的权值
        adjvex[i] = 0;                                  //顶点v0
    }                                                   //初始化完成

    //step2:寻找下一个加入生成树的顶点
    int k;
    for (int i = 1; i < G.numVertexes; i++)             //遍历每个顶点
    {
        int min = MAXEDGE;                              //初始化最小权值为无穷大

        for (int j = 1; j < G.numVertexes; j++)         //遍历lowcost寻找最小权值
        {
            if (lowcost[j] && lowcost[j] < min)         //如果该顶点还未加入生成树
            {
                min = lowcost[j];
                k = j;                                  //找到最小权值边对应的顶点
            }
        }

        cout << "(" << adjvex[k] << ","                 //输出最小权值的边
            << k << ")" <<
            "(" << G.vexs[adjvex[k]] << ","                 //输出最小权值的边
            << G.vexs[k] << ")" << endl;

        //step3:更新已加入生成树的顶点与其它顶点之间的最小值
        for (int m = 1; m < G.numVertexes; m++)
        {
            if (lowcost[m] && G.arc[k][m] < lowcost[m]) //新加入的顶点与其它顶点之间的边的最小值是否足够小
            {
                lowcost[m] = G.arc[k][m];
                adjvex[m] = k;                          //新加入的顶点k与m之间的边为候选
            }
        }
    }
}

//克鲁斯卡尔算法
//将邻接矩阵转换为边集数组并按照权值升序排序
void MGraph2Edge(MGraph MG, Edge edges[])
{
    int k = 0;                      //记录插入个数
    for (int i = 0; i < MG.numVertexes; i++)
    {
        for (int j = 0; j < i; j++)
        {
            int weight = MG.arc[i][j];
            if (weight > 0 && weight < MAXEDGE)
            {
                Edge edge;

                edge.begin = i;
                edge.end = j;

                edge.weight = weight;
                if (k == 0)         //第一个直接插入
                {
                    edges[k] = edge;
                }
                else
                {
                    int n = k - 1;                      //最后一个元素的位置
                    if (weight < edges[n].weight)       //如果待插入元素小于已经排好序的有序数组中最后一个元素
                    {
                        for (; weight < edges[n].weight; n--)
                        {
                            edges[n + 1] = edges[n];
                        }
                    }
                    edges[n + 1] = edge;                //插入

                }
                k++;
            }
        }
    }
}

//Kruskal算法生成最小生成树
void MiniSpanTree_Kruskal(MGraph G)
{
    Edge edges[MAXEDGE] = { 0 };                        //边集数组
    int parent[MAXVEX] ;                            //用于判断是否形成环
    for (int i = 0; i < G.numVertexes; i++)
    {
        parent[i] = -1;
    }

    MGraph2Edge(G, edges);                              //计算边集数组并按照权值升序排序

    for (int i = 0; i < G.numEdges; i++)                //遍历每一条边
    {
        int n = Find(parent, edges[i].begin);           //寻找此边的顶点begin是否在生成树中
        int m = Find(parent, edges[i].end);             //寻找此边的顶点end是否在生成树中

        if (n != m)                                     //不相等说明此边没有与现有生成树形成环路,两个顶点在不同的连通分量上
        {
            parent[n] = m;                              //将此边的结尾顶点放入下标为起点的parent中,表示顶点n,m已经在生成树集合中,
            cout << "(" << edges[i].begin << "," << edges[i].end << ")  " << edges[i].weight << endl;
        }
    }
}

//查找连线顶点的尾部下标,判断顶点f是否已经连接到生成树中
int Find(int* parent, int f)
{
    while (parent[f] > -1)
    {
        f = parent[f];
    }
    return f;
}

main.cpp

#include <iostream>
#include "MGraph.h"
#include "ALGraph.h"

using namespace std;

extern bool* visited, * visited1;

int main(int argc, char** argv)
{
    //1.邻接矩阵
    MGraph MG;
    CreateMGrah(MG);

    for (int i = 0; i < MG.numVertexes; i++)    //对于连通图,只执行一次
    {
        if (!visited[i])
        {
            DFS(MG, i);                         //深度优先搜索遍历
            //BFS(MG, 0);                       //广度优先搜索遍历
        }
    }

    cout << "\nPrim 最小生成树:" << endl;
    /*
    A D 1
    A E 3
    A B 7
    B C 5
    B E 2
    C D 6
    C E 8
    D E 4
    */
    MiniSpanTree_Prim(MG);

    cout << "\nKruskal 最小生成树:" << endl;
    //Edge edges[MAXEDGE] = { 0 };
    //MGraph2Edge(MG, edges);
    MiniSpanTree_Kruskal(MG);

    return 0;
}
上一篇下一篇

猜你喜欢

热点阅读