普里姆算法

基本思想:以某个顶点为起点,逐步找各顶点上最小权值的边来构建最小树。

代码如下

vector<vector<int>> edges;//邻接矩阵
int N;//N为顶点数
//普里姆算法,从编号为0顶点开始
void prim()
{vector<int> index(N, 0);//储存对应顶点的下标,初始化为第一个顶点的下标0vector<int> lowcost(edges[0]);//表示已加入生成树的顶点到其他顶点之间权值的最小值,如果为INT_MAX表示它们之间没有边lowcost[0] = 0;//lowcost[i] = 0, 表示编号为i的顶点已加入生成树,这里把第一个顶点加入生成树for (int i = 1; i < N; ++i)//找到N-1条边{//找到没有加入到生成树中且最小的权值的顶点下标curint Min = INT_MAX, cur = 0;for (int j = 1; j < N; ++j){if (lowcost[j] != 0 && lowcost[j] < Min){/*lowcost[j]!=0表示没有加入到生成树*/Min = lowcost[j];cur = j;}}cout<<"("<<index[cur]<<","<<cur<<")"<<endl;//这就是生成树其中的一条边,将它输出lowcost[cur] = 0;//表示将编号为cur的顶点加入生成树//更新lowcastfor (int j = 1; j < N; ++j){/*将当前顶点与其他没被加入顶点之间的权值与lowcost中对应的权值比较,若小于加入待用*/if (lowcost[j] != 0 && edges[cur][j] < lowcost[j]){lowcost[j] = edges[cur][j];index[j] = cur;//储存下标}} }
}

下面以一个例子来介绍算法该算法流程:

第一次循环:此时v0已加入最小树,lowcost储存着(v0, v1), (v0, v5)的权值,找到(v0, v1)的的权值最小,将v1加入生成树,(v0, v1)即是生成树的一条边。然后更新lowcost,将v1与其他没被加入生成树顶点之间权值拿来比较,加入lowcost备用,此时lowcost储存着(vo, v5), (v1, v2), (v1, v8), (v1, v6)的权值。
第二次循环:遍历找到(v0, v5)的权值最小,把v5加入最小树,得到生成树的一条边(v0, v5),然后继续更新lowcost,把v5与其他没被加入生成树顶点之间权值拿来比较,加入lowcost备用,此时lowcost储存(v1, v2), (v1, v8), (v1, v6), (v5, v6), (v5, v4)的权值。
如此反复,经过不断地转换与构造最终得到生成树所有的边。

克鲁斯卡尔算法

算法思想:通普里姆算法一样的思路,只不过是直接以边为目标去构建,直接去找最小权值的边来构造最小生成树,但要考虑是否会构成回路。
这里要用到图的储存结构中边集数组结构

struct edge
{int Begin;int weight;int End;edge(int b = 0, int w = 0, int e = 0):Begin(b), weight(w), End(e){}bool operator < (edge& e){return weight < e.weight;}
};

这里不再赘述。
代码如下

struct edge
{int Begin;int weight;int End;edge(int b = 0, int w = 0, int e = 0):Begin(b), weight(w), End(e){}bool operator < (edge& e){return weight < e.weight;}
};int N, E;//顶点数和边数。int Find(vector<int>& p, int f)//查找集合中从f开始的终点
{while (p[f] > 0) f = p[f];return f;
}void kruskal(vector<edge>& a)
{sort(a.begin(), a.end());//按权值从小到大排序vector<int> parent(N, 0);//用来判断是否构成回路,其详细作用后面会讲到for (int i = 0; i < E; ++i){int m = Find(parent, a[i].Begin);int n = Find(parent, a[i].End);if (m != n)//如果m==n表示a[i]边的两个顶点在同一个生成树边集和里,如果相连就会构成回路{cout<<a[i].Begin<<" "<<a[i].End<<endl;//得到了生成树的一条边parent[m] = n;//将a[i]边尾顶点放到下标为起点的parent中}}
}

拿上述例子介绍该算法流程
排序后的数组a

Begin End weight
a[0] 4 7 7
a[1] 2 8 8
a[2] 0 1 10
a[3] 0 5 11
a[4] 1 8 12
a[5] 3 7 16
a[6] 1 6 16
a[7] 5 6 17

1.i = 0时得到通过调用Find得到m = 4, n = 7,他们不相等所以为生成树的一条边。并使parent[4] = 7,表示4和7在同一个生成树集里。
2.i = 1时得到m = 2,n = 8,他们不相等所以为生成树的一条边。并使2,8在同一个生成树集里。
3.i =2,3,4,5,6时同理,这时得到parent为[1,5,8,7,7,8,0,0,6]这个数组表示了目前两个生成树边连通集A,B,parent[0] = 1表示v0,v1在A中,然后以1为下标parent[1] = 5表示v1,v5在连通集A中,parent[5] = 8表示v5,v8在A中,parent[8] = 6,表示v8,v6在A中,parent[6] = 0说明A已经到头,又可以发现parent[2]=8,所以v2,v8也在A中,此时边集A中有v0,v1,v5,v8,v6,v2。同理B中有v3,v4,v7。
4.i = 7时,得到m=n=6,所以表示a[7]边的顶点5,6再一个边集中,如果连接则会构成回路,因此跳过。

此后如此反复找到所有生成树的边。

二者之间的比较:

普里姆算法的时间复杂度为O(n^2),n为顶点数。其主要针对顶点展开,在边较多的稠密图时较好。
库鲁斯卡尔算法的时间复杂度为eloge,e为边数。其主要针对边展开,在边较少的稀疏图时效率很高。

例题:

7-10 公路村村通 (30分)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本。

输入格式:
输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N);随后的M行对应M条道路,每行给出3个正整数,分别是该条道路直接连通的两个城镇的编号以及该道路改建的预算成本。为简单起见,城镇从1到N编号。

输出格式:
输出村村通需要的最低成本。如果输入数据不足以保证畅通,则输出−1,表示需要建设更多公路。

输入样例:
6 15
1 2 5
1 3 3
1 4 7
1 5 4
1 6 2
2 3 4
2 4 6
2 5 2
2 6 6
3 4 6
3 5 1
3 6 1
4 5 10
4 6 8
5 6 3
输出样例:
12

解法一:普里姆算法

#include <bits/stdc++.h>using namespace std;vector<vector<int>> edges;
int N, E, ans = 0;bool prim()
{vector<int> index(N+1, 1);vector<int> lowcast(edges[1]);lowcast[1] = 0;for (int i = 1; i < N; ++i){int Min = INT_MAX, cur = -1;for (int j = 2; j <= N; ++j){if (lowcast[j] != 0 && lowcast[j] < Min){Min = lowcast[j];cur = j;}}if (cur == -1) return 0;ans += edges[index[cur]][cur];lowcast[cur] = 0;//更新lowcastfor (int j = 2; j <= N; ++j){if (lowcast[j] != 0 && edges[cur][j] < lowcast[j]){lowcast[j] = edges[cur][j];index[j] = cur;}} }return 1;
}int main()
{cin>>N>>E;edges.resize(N+1);for (int i = 1; i <= N; ++i){vector<int> tmp(N+1, INT_MAX);edges[i] = tmp;}for (int i = 1; i <= E; ++i){int v1, v2, x;cin>>v1>>v2>>x;edges[v1][v2] = x;edges[v2][v1] = x;}cout<<(prim() == 1? ans:-1);return 0;
}

解法二:库鲁斯卡尔算法

#include <bits/stdc++.h>using namespace std;struct edge
{int Begin;int weight;int End;edge(int b = 0, int w = 0, int e = 0):Begin(b), weight(w), End(e){}bool operator < (edge& e){return weight < e.weight;}
};int N, E, ans = 0, cnt = 0;int Find(vector<int>& p, int f)
{while (p[f] > 0) f = p[f];return f;
}void kruskal(vector<edge>& a)
{vector<int> parent(N+1, 0);for (int i = 1; i <= E; ++i){int m = Find(parent, a[i].Begin);int n = Find(parent, a[i].End);if (m != n){ans += a[i].weight;++cnt;parent[m] = n;}}
}int main()
{cin>>N>>E;if (E < N - 1){cout<<-1;return 0;}vector<edge> a(E+1);for (int i = 1; i <= E; ++i){int v1, v2, x;cin>>v1>>v2>>x;a[i].Begin = v1;a[i].End = v2;a[i].weight = x;}sort(a.begin()+1, a.end());kruskal(a);cout<<(cnt == N-1? ans:-1);return 0;
}

生成最小树(普里姆算法与库鲁斯卡尔算法)相关推荐

  1. java实现 库鲁斯卡尔算法 kruskal(算法设计与分析作业)

    java实现 库鲁斯卡尔算法kruskal(算法设计与分析作业) package package1;import java.util.Arrays;public class alg {//判断tar是 ...

  2. 最小生成树(库鲁斯卡尔算法)

    #include<iostream> #include<algorithm> using namespace std; #define maxSize 100 #define ...

  3. 最小生成树普里姆算法c语言代码,普里姆算法生成最小生成树-C语言描述.doc

    PAGE JIN JINGCHU UNIVERSITY OF TECHNOLOGY <数据结构(C语言描述)> 课程设计 学 院 计算机工程学院 班 级 12级软件技术1班 学 号 201 ...

  4. 数据结构与算法(7-3)最小生成树(普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法)

    目录 一.最小生成树简介 二.普里姆算法(Prim) 1.原理 2.存储 2-1.图顶点和权: 2-3. 最小生成树: 3.Prim()函数 3-1.新顶点入树 3-2.保留最小权 3-3. 找到最小 ...

  5. 最小生成树(普里姆算法【Prim】与克鲁斯卡尔算法【Kruskal】)

    写在前面:博主是一位普普通通的19届双非软工在读生,平时最大的爱好就是听听歌,逛逛B站.博主很喜欢的一句话花开堪折直须折,莫待无花空折枝:博主的理解是头一次为人,就应该做自己想做的事,做自己不后悔的事 ...

  6. 普里姆算法(修路问题)+图解

    图解 代码实现 package com.atguigu.prim;import java.util.Arrays;/*** @创建人 wdl* @创建时间 2021/4/5* @描述*/ public ...

  7. 算法 - 普里姆算法(修路问题求解)

    从A开始,A作为定点,找到与A相连并且未被处理(不在顶点集合中)的进行处理,找到权值最小的并加入集合,A-C[7].A-G[2].A-B[5],最小的是A-G[2],所以把G加入集合,这里是有A-G的 ...

  8. 最小生成树——普里姆算法和克鲁斯卡尔算法

    最小生成树 用来解决工程中的代价问题. 一:普里姆算法 具体代码用C语言实现如下: typedef int VRType;typedef char InfoType;#define MAX_NAME ...

  9. 最小生成树之普里姆算法

    为了能够讲明白这个算法,我们先构造网图的邻接矩阵,如图7-6-3的右图所示. 也就是说,现在我们已经有了一个存储结构为MGraph的MG(见<邻接矩阵创建图>).MG有9个顶点,它的二维数 ...

最新文章

  1. 人工智能缺陷与误觉:让机器产生幻觉的「怪异事件」
  2. WINCE6.0+S3C6410睡眠和唤醒的实现
  3. C++中对于类来说头文件(.h)和源文件(.cpp)都应该写些什么 (类的常规创建)
  4. 织梦网站翻页php,dedecms织梦网站列表页和内容页分页样式
  5. C和指针之函数之求最大公约数
  6. gradle构建工具_Gradle:我们需要另一个构建工具吗?
  7. 551. Student Attendance Record I 从字符串判断学生考勤
  8. 虚拟专题:联邦学习 | 联邦学习算法综述
  9. C++容器(一)——vector
  10. kafka权威指南-笔记
  11. 周志明:终于薅住了这位 “社恐”作者的小辫子
  12. c#语言猜数字游戏,C#实现简易猜数字游戏
  13. afrog 发布新版 Release 1.3.6 漫天星辰
  14. vue中img本地图片地址的具体使用
  15. Android中startActivities的准确用法
  16. Android心电数据分析,Android 根据心电图(ECG)数据分析绘制心电图
  17. easyUi input标签的失去焦点事件
  18. 2022-2027年中国中医药行业发展监测及投资战略研究报告
  19. 读书笔记 | 自动驾驶中的雷达信号处理(第6章 到达方向(DOA)估计算法 )
  20. EXCEL-在EXCEL中批量修改超链接方法

热门文章

  1. vs2019无法打开包括文件
  2. 怎么pdf转cad?推荐这个简单方法,一键转换多个文件
  3. MySQL查询所有子文件目录_使用File类的listFiles方法输出d盘根目录下的所有子目录和文件的完整路径名以及文件的字节数。写出相应代码。...
  4. 【密码产品篇】VPN产品密钥体系结构
  5. SSL certificate problem: unable to get local issuer certificate解决办法
  6. wifinbsp;驱动nbsp;进阶11
  7. hpdl380g9没有f10,hpdl380g9raid配置
  8. ADSP-21489的图形化编程详解(3:音效开发例程-直通三个例程讲清楚)
  9. iconfont的基本使用
  10. 文献精读(1)MRD,NSCLC