数据结构学习——最小生成树之Prim算法和Kruskal算法

本人是帝都某高校计算机专业学生,最近在学习数据结构,想把自己学习的感想和收获以及一些疑惑和思考写下来与算法和数据结构初学者们分享,如果本博客有问题欢迎指正。

本博客主要围绕下面四个内容展开。

1.Prim和Kruskal算法的简单概述
2.用最朴实的口语描述我理解中的Prim算法
3.用最朴实的口语描述我理解中的Kruskal算法
4.例题分析

1.Prim和kruskal算法的简单概述
这两种算法的历史啊,发展啊啥的我也不科普了,这对学习这两种算法没有什么实质性帮助,我直接说这两种算法。这两种算法本质上都属于贪心算法,也就是存在最优子结构,能够通过选择当前最优解的策略来获取全局最优解!

2.Prim算法
Prim算法的思想就是:先选择所有边中权值最小的,让这条边以及它的两个顶点形成一棵树T,然后看看其它未加入该树的点与这棵树里任意一个顶点所拥有的路径,选择这些路径中权值最小的,连起来,这样树T中就多了一个顶点。一直重复该步骤直至所有顶点都被加入到树中。
在这里插入图片描述
3.Kruskal算法
Kruskal算法的意思是:先把所有顶点看成各个独立的树,这样就形成了一个森林,在这个森林中选择连接两棵树(必须是两棵树,不能是树内两个顶点之间的边)的权值最小的边,将这条边连起来,同时这两棵树合成一棵树。循环该过程知道左右顶点都连成同一棵树。注意这里面单独一个顶点也是树。
在这里插入图片描述
4.例题分析
某地区在地震过后,连接所有村庄的公路都造成了损坏而无法通车,政府派若干个工程队同时修复这些公路。给出该地区的村庄数 ,和公路数 ,公路是双向的。并告诉你每条公路的连着哪两个村庄,什么时候能修完这条公路,问最早什么时候任意两个村庄能够通车。

在这里插入图片描述
在这里插入图片描述
这个题目我用的是Prim算法写的,野路子出身,并没有套用什么Prim算法代码模板啥的,纯粹是自己想自己写的,因此读起来可能不符合大众口味,还望见谅!

#include<bits/stdc++.h>
using namespace std;
struct side {
 int d1;
 int d2;
 int time;
}edge[100005];     //定义边的结构体,包含三个信息:边的两个定点城市编号以及权值,定义edge数组存放这些边
int record[1005];   //record[i]=1表示城市i已经加入树中,=0表示没有加入
int sum(int n)
{
 int su = 0;
 for (int i = 1; i <= n; i++)
 {
  su += record[i];
 }
 //sum函数是计算record数组的和,也就是判断是否所有城市已经加入到树中。返回值为n(输入的城市数)就表示所有城市都连通了。
 return su;
}
bool cmp(side a, side b)
{
 return a.time < b.time;
}
//sort排序函数中的cmp,不懂的可自行百度sort函数的用法
int main()
{
 vector<int>Time;   //记录最小生成树所有边的权值(也就是修每条路所用的时间)
 int n, m;        //城市数,路数
 cin >> n >> m;
 int i = 0;
 int j = m;
 while (j--)
 {
  int c1, c2, t;
  cin >> c1 >> c2 >> t;
  edge[i].d1 = c1;
  edge[i].d2 = c2;
  edge[i].time = t;
  i++;
 }
 //该循环将所有边构造好并放入数组中,此时所有边没有按照权值大小排序
 
 sort(edge, edge + i, cmp);      //对数组按照time大小排序
 record[edge[0].d1] = 1;
 record[edge[0].d2] = 1;
 Time.push_back(edge[0].time);
 //选择time最小的边形成一颗只有两个节点的树,并将该边的time放入vector中
 
 int counting = 2;
 int max_loop = 0;
 //counting记录目前加入了多少顶点,max_loop表示最大循环次数,由于一次循环将加入一个顶点到树中,一共也就最多1000个城市,因此如果循环1500次counting仍然没有达到n,就说明该组数据不可能生成一棵树。
 
 while (counting < n&&max_loop < 1500)
 {
  for (int i = 0; i < m; i++)    //此时edge已经按照time权值排好序,从小到大,因此每次都从头开始循环
  {
   if ((record[edge[i].d1] == 1||record[edge[i].d2] == 1))
   {
    if ((record[edge[i].d1] != 1 || record[edge[i].d2] != 1))
    {
    //上面那两个if就是判断该边是否一个顶点在树中,一个顶点不在树中
     Time.push_back(edge[i].time);
     counting++;
     record[edge[i].d1] = 1;
     record[edge[i].d2] = 1;
     break;
     //把一个边加入到树中,就跳出循环,进行下一次
    }
   }
  }
  max_loop++;
 }
 if (max_loop == 1500)
  cout << -1 << endl;
 else
  cout << *max_element(Time.begin(),Time.end()) << endl;     //不清楚*max_element函数的亦可以百度
 return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值