prim算法

挺久之前就学会了kruskal了,但是因为这个算法看起来非常的麻烦,没理解 ,所以一直到最近数据结构学习才认真的研究了一下,懂了之后其实挺简单的。不过要记住的是,只有无向网图才有生成树
首先先文字讲一下过程吧,然后在配合图片进行讲解。
我们已知一个图的顶点集合为 U, 和一个已选取点的集合V。然后随机选取一个顶点,从U中删除,并加入到V中以它作为树根进行操作。
接下来就是一个循环操作,每次都找到集合中所有点和不在集合中的点的最小边,sum加上它的权重,并把这个点加入到V中,并从U中删除。直到所有的点都在V中循环结束。
来一波图解,文字可能许多人不懂。
在这里插入图片描述
对于上面的图来说,U集合为{1, 2, 3, 4, 5}。我们随机选取一个点,比如说选取1,并把它加入到V集合,所以U={2, 3, 4, 5}, V = {1}。
然后找到和1相连的点最小的权重值为2,因此把顶点2加进来。现在的sum=2。
在这里插入图片描述
上图红色圈内的点为V集合内的点,接下来在看V集合中所有的点和不在V集合中的点最小的权重是多少,很轻松找到了3,然后把4加到V集合,并从U集合减去4。sum = 5
在这里插入图片描述
循环上面的过程,找到了权重为1的边,和它相连的是顶点3,所以把顶点3加进来,并从U中删除。sum =6
在这里插入图片描述
最后找到权重为4的边,并且把5加进来。sum = 10
在这里插入图片描述
到此为止我们就找到了最小生成树,并且它的权重为10。

到现在为止思想我们就讲完了,在说一点实现的问题。
1),V集合其实就是一个标记数组vis,标记了就代表加入到V集合了,并且从U中删除了。
2),就是每次如何去找这个权重最小的边呢?这里它并不是暴力取遍历整个V集合,而是从一开始就有一个dis数组,它里面存的是当前V集合中的所有最小的权重,并且每次新加入一个顶点就判断它能不能更新dis数组。比如上图一开始我们选择顶点1,所以dis = {INF, 2, INF, 3, 6}(INF代表无穷远)。当选择顶点2后,dis = {INF, 2, 9, 3, 6}。实际就是把所有V中的顶点进行缩点,看作一个大点。因此一开始不能到达的顶点3也可以到达了。

#include <iostream>
#include <vector>
#include <cstring>

using namespace std;

const int MAX_N = 50001;
const int INF = 0x3f3f3f3f;

int dis[MAX_N], vis[MAX_N];

struct edge
{
    int to, cost;

    edge(int t, int c):to(t), cost(c)
    {}
};

vector<edge> G[MAX_N];

void add_edge(int from, int to, int cost)
{
    G[from].push_back(edge(to, cost));
    G[to].push_back(edge(from, cost));
}

int prim(int v, int n)
{
    memset(dis, INF, sizeof(dis));
    vis[v] = true;

    int sum = 0;
	//把自己选取的第一个点周围的边赋值给dis数组
    for (int i=0; i<G[v].size(); i++)
    {
        dis[G[v][i].to] = G[v][i].cost;
    }
	//只需要循环n-1次就可以了,因为自己设置的顶点已经处理过了
    for (int i=1; i<n; i++)
    {
        int index = 0, minnum = INF;
		//找到当前dis数组内最小边
        for (int j=1; j<=n; j++)
        {
            if (!vis[j] && dis[j] < minnum)
            {
                minnum = dis[j];
                index = j;
            }
        }
		
        vis[index] = true;
        sum+=minnum;
		//检查新加进来的点和其他没加进来的点的权重能不能更新dis数组
        for (int j=0; j<G[index].size(); j++)
        { 	
            if (!vis[G[index][j].to] && dis[G[index][j].to] > G[index][j].cost)
            {
                dis[G[index][j].to] = G[index][j].cost;
            }
        }
    }

    return sum;
}

int main()
{
    int n, m;

    cin >> n >> m;

    for (int i=0; i<m; i++)
    {
        int x, y, cost;

        cin >> x >> y >> cost;
        add_edge(x, y, cost);
    }
	//随便选取一个点进行操作
    cout << prim(1, n) << endl;

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值