最小生成树之普里姆 Prim算法

定义介绍及分析

生成树的定义
一个连通图的生成树是一个极小的连通子图,它包含图中全部的n个顶点,但只有构成一棵树的n-1条边。
生成树的属性
一个连通图可以有多个生成树;
一个连通图的所有生成树都包含相同的顶点个数和边数;
最小生成树
一个带权图的最小生成树,就是原图中边的权值最小的生成树 ,所谓最小是指边的权值之和小于或者等于其它生成树的边的权值之和。

如下图:
在这里插入图片描述
在以a,b,c,d,e,f,g为顶点的网中,我们假设从起点c出发,去寻找一个最小生成树。

1.从起点c开始,有c-b和c-d两条边,我们选择权值较小的边,即c-b边;
2.接下来,对于已访问的点b,c,我们继续去寻找根b,c相连的边,选择其中最小权值的一条边,即b-a边。
3.对于已访问的点c,b,a,继续按照相同的方法去寻找。
以此类推
.
.
.
.
.
最终,当所有顶点都访问完后,我们便得到了其中一棵最小生成树(因起点选择而异)

最小生成树应用举例

在这里插入图片描述
在这里插入图片描述

Prim算法

Prim算法是一种用于解决最小生成树(Minimum Spanning Tree)问题的贪心算法。最小生成树是一个无向连通图中的一棵包含图中所有顶点的树,且树的所有边的权值之和最小。

以下是Prim算法的具体介绍步骤:

  1. 初始化:选择一个起始顶点作为最小生成树的根节点,将其加入最小生成树集合中,并将与该节点相邻的边加入边集合中。

  2. 重复步骤:重复以下步骤直到最小生成树包含所有顶点:

    • 从边集合中选择权值最小且连接已选顶点集合和未选顶点集合的边。
    • 将该边连接的未选顶点加入最小生成树集合中,并将与该顶点相邻的边加入边集合中。
  3. 终止条件:当最小生成树包含了图中所有的顶点时,算法终止。

  4. 最小生成树构建:最终得到的最小生成树即为算法的输出。

以下是Prim算法的伪代码:

Prim(G, start):
    Initialize an empty set MST to store the minimum spanning tree
    Initialize an empty set visited to keep track of visited vertices
    Initialize a priority queue (min-heap) Q to store edges with their weights

    Add start vertex to visited
    Add all edges connected to start vertex to Q

    while Q is not empty:
        (u, v) = ExtractMin(Q)  // Extract edge with minimum weight
        if v is not in visited:
            Add v to visited
            Add edge (u, v) to MST
            Add all edges connected to v to Q

    return MST

Prim算法的时间复杂度为O(V^2)或O(E*log(V)),其中V是顶点数,E是边数。在稀疏图(边数接近顶点数)的情况下,使用最小堆实现的Prim算法更有效率。Prim算法的优点是简单易懂,适用于稠密图和稀疏图。

其实,Prim算法的过程就是我上面举的那个例子,只不过将其中的过程用代码语言来表示。如果伪代码看不懂,那么请看我下面对Prim算法的实现。

算法实现

#include <iostream>
using namespace std;

#define Maxvexnum 100
#define Maxint 0x3f3f3f
#define OK 1

typedef struct Gragh
{
    char vex[Maxvexnum];//顶点表
    int arc[Maxvexnum][Maxvexnum];//邻接矩阵
    int vexnum, arcnum;//顶点数、边数
}Gragh;

typedef struct SE
{
    char adjvex;
    int lowcost;
};

int Locate_vex(Gragh G,char c)
{
    for (int i = 0; i < G.vexnum; i++)
    {
        if (c == G.vex[i])  return i;
    }
    return -1;
}

int Creat_gragh(Gragh &G)
{
    int vexnum, arcnum;
    cin >> vexnum >> arcnum;  //输入顶点数、边数
    G.vexnum = vexnum;
    G.arcnum = arcnum;

    //顶点表赋值
    for (int i = 0; i < G.vexnum; i++)
    {
        cin >> G.vex[i];
    }
    
    //初始化邻接矩阵
    for (int i = 0; i < G.vexnum; i++)
    {
        for (int j = 0; j < G.vexnum; j++)
        {
            if (i == j) G.arc[i][j] = 0;
            else G.arc[i][j] = Maxint;
        }
    }

    //邻接矩阵赋值
    for (int i = 0; i < G.arcnum; i++)
    {
        char v1, v2;
        int w;
        cin >> v1 >> v2 >> w;
        int m = Locate_vex(G, v1);
        int n = Locate_vex(G, v2);
        G.arc[m][n] = G.arc[n][m] = w;
    }

    return OK;
}

void Print(Gragh G) //打印顶点表和邻接矩阵
{
    for (int i = 0; i < G.vexnum; i++)
    {
        cout << G.vex[i] << ' ';
    }
    cout << endl;
    for (int i = 0; i < G.vexnum; i++)
    {
        for (int j = 0; j < G.vexnum; j++)
        {
            if (G.arc[i][j] == Maxint)
                cout << "∞" << ' ';
            else
                cout << G.arc[i][j] << ' ';
            if (j == G.vexnum - 1)
                cout << endl;
        }
    }
}

int minEdge(SE* array, int n) //找最小边
{
    int min = Maxint;
    int mark;
    for (int i = 0; i < n; i++)
    {
        if (array[i].lowcost != 0 && array[i].lowcost < min)
        {
            min = array[i].lowcost;
            mark = i;
        }
    }
    return mark;
}

void output_SMT(int k, SE* array, char c)
{
    cout << "(" << array[k].adjvex << "," << c << ")" << ' ' << array[k].lowcost << endl;
}

void Prim(Gragh G) //coredemo
{
    int start, k;
    char c;
    SE* shortEdge;
    shortEdge = new SE[G.vexnum];

    //对辅助数组进行初始赋值
    cin >> c; //输入一个起点
    start = Locate_vex(G, c);
    for (int i = 0; i < G.vexnum; i++)
    {
        shortEdge[i].adjvex = G.vex[start];
        shortEdge[i].lowcost = G.arc[start][i];
    }

    shortEdge[start].lowcost = 0;
    
    for (int i = 0; i < G.vexnum - 1; i++)
    {
        k = minEdge(shortEdge, G.vexnum);
        output_SMT(k, shortEdge, G.vex[k]);
        shortEdge[k].lowcost = 0;
        for (int j = 0; j < G.vexnum; j++)
        {
            if (G.arc[k][j] < shortEdge[j].lowcost)
            {
                shortEdge[j].lowcost = G.arc[k][j];
                shortEdge[j].adjvex = G.vex[k];
            }
        }
    }
    delete[]shortEdge;
}

int main()
{
    Gragh G;
    Creat_gragh(G);
    Print(G);
    cout << endl;
    cout << "最小生成树路径:" << endl;
    Prim(G);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

推荐教程

若有疑问的地方,建议观看视频:
懒猫Prim1
懒猫Prim2

Over!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值