赞
踩
by.Qin3Yu
请注意:阅读本文需要您先掌握图、对、顺序表以及优先队列的基本操作,具体可参阅我的往期博客:
【C++数据结构 | 顺序表速通】使用顺序表完成简单的成绩管理系统.by.Qin3Yu
【C++数据结构 | 队列速通】图解BFS算法走迷宫最短路径问题.by.Qin3Yu
文中所有代码使用 C++ 举例,且默认已使用 std 命名空间:
using namespace std;
针对本文中的部分代码案例,还需要额外导入以下头文件:
#include <vector>
#include <queue>
其他概念定义:
- 环: 由n个顶点和n-1条边组成的闭合回路,如上图中的A-B-E-F-C就是一个环。
- 稠密图: 相对于点数,边数较多的图。
- 稀疏图: 相对于边数,点数较多的图。
Prim算法由美国计算机科学家罗伯特·C·普里姆(Robert C. Prim)于1957年提出。其基于贪心策略(Greedy Strategy)实现,和我们以后会讲的Dijkstra算法非常相似。它的具体实现方法可以概括为如下:从起始点开始向外扩散,寻找权最小的邻边,并将边另一侧的点加入集合,已在集合内的点不能再次加入(否则会成环),直到所有的点都加入了集合,则构成了最小生成树。
Prim算法每次选择连接生成树和非生成树顶点的最小权值边,这种局部最优的选择确保了最终生成树的全局最优性。因此,Prim算法能够在保证最小生成树的同时,保证较好的时间复杂度。此外,prim算法还能保证最小生成树的连通性。
时间复杂度: O(mlogn),m为边数,n为点数。
最佳适用情景: 稠密图或边的权值比较平均的图。
Kruskal算法由美国计算机科学家乔治·P·克鲁斯卡尔(George P. Kruskal)于1956年提出。其同样是通过贪心算法来实现的,但同时还需要 并查集(在后文会做出详细讲解) 来辅助操作。它的具体实现方法可以概括为如下:从连通图的权最小边开始,不断访问相邻的权最小边(前提是不能成环),最后将其合并,其中需要利用并查集来辅助操作。
与prim算法不同的是,Kruskal算法直接基于边开始运算,所以其时间复杂度与点数无关。Kruskal算法不需要图是连通的,因此可以应用于处理非连通图的最小生成树。
时间复杂度: O(mlogm),m为边数。
最佳适用情景: 稀疏图或边的权值差值较大的图。
- 找到最小权临边;
- 将边另一端的点加入集合。
- 找出最小权边,检查边两端的点是否处于同一个集合
- 若不处于同一个集合,则将它们合并为同一个集合
ps.相连的蓝色区块算作一个集合
本篇文章仅会做出prim算法的代码详解,kruskal算法的代码详解之后会发出。
//分别代表边的终点和权值,并用typedef改写为pii
typedef pair<int, int> pii;
// 顶点数
int n = 7;
//使用顺序表保存多个pii
vector<vector<pii>> graph(n);
//参数分别表示起点、终点和权值
graph[0].push_back(make_pair(1, 7));
graph[1].push_back(make_pair(0, 7));
......
graph[5].push_back(make_pair(6, 2));
graph[6].push_back(make_pair(5, 2));
//记录当前点是否被访问过并使用bool表示,默认false(未访问)
vector<bool> visited(n, false);
//代表无穷大,方便后续进行小于判断
const int INF = 1e9;
//记录点到起始点的距离
vector<int> dist(n, INF);
//记录最小生成树的权值和
int minCost = 0;
//优先队列 pq,权值作为优先因子
priority_queue<pii, vector<pii>, greater<pii>> pq;
//将起点放入,其的终点、边权和到起始点的距离均为0
pq.push(make_pair(0, 0));
dist[0] = 0;
while (!pq.empty()) {
//取出当前权值最小的顶点u,获取u的索引
int u = pq.top().second;
pq.pop();
//将顶点u标记为已访问
visited[u] = true;
......
}
while (!pq.empty()) { ...... //遍历与顶点u相连的边 for (auto& edge : graph[u]) { //记录边的终点 int v = edge.first; //记录边的权值 int weight = edge.second; //判断顶点 v 是否未被访问过且边的权值小于顶点 v 当前存储的权值 if (!visited[v] && weight < dist[v]) { //更新顶点 v 的权值为边的权值 dist[v] = weight; //将更新后的顶点 v 及其权值加入优先队列 pq.push(make_pair(dist[v], v)); } } }
Q国现有一个核发电站,需要依靠此发电站向A、B、C、D、E、F、G七个城市供电,各个城市与发电站之间的可用供路与距离如图所示,如何规划电路,最大程度上节约电缆成本?最少需要多少千米的电缆(不记城市内部的电缆消耗)?
本代码案例将使用下图为例,计算最小生成树的边权之和:
#include <iostream> #include <vector> #include <queue> using namespace std; const int INF = 1e9; //代表无穷大 typedef pair<int, int> pii; //对(pair),分别代表边的终点和权值 int PRIM(vector<vector<pii>>& graph, int n) { vector<bool> visited(n, false); //记录当前点是否被访问过 vector<int> dist(n, INF); //记录点到起始点的距离 int minCost = 0; //记录最小生成树的权值和 priority_queue<pii, vector<pii>, greater<pii>> pq; //优先队列 pq,权值作为优先级 pq.push(make_pair(0, 0)); //将起点放入 dist[0] = 0; while (!pq.empty()) { int u = pq.top().second; //取出当前权值最小的顶点u,获取u的索引 pq.pop(); visited[u] = true; //将顶点u标记为已访问 //遍历与顶点u相连的边 for (auto& edge : graph[u]) { int v = edge.first; //边的终点 int weight = edge.second; //边的权值 //判断顶点 v 是否未被访问过且边的权值小于顶点 v 当前存储的权值 if (!visited[v] && weight < dist[v]) { dist[v] = weight; //更新顶点 v 的权值为边的权值。 pq.push(make_pair(dist[v], v)); //将更新后的顶点 v 及其权值加入优先队列 } } } //计算和 for (int i = 0; i < n; i++) minCost += dist[i]; return minCost; } int main() { int n = 7; // 顶点数 vector<vector<pii>> graph(n); // 起点 终点 权值 graph[0].push_back(make_pair(1, 7)); graph[1].push_back(make_pair(0, 7)); graph[0].push_back(make_pair(2, 6)); graph[2].push_back(make_pair(0, 6)); graph[0].push_back(make_pair(5, 4)); graph[5].push_back(make_pair(0, 4)); graph[0].push_back(make_pair(6, 3)); graph[6].push_back(make_pair(0, 3)); graph[1].push_back(make_pair(2, 8)); graph[2].push_back(make_pair(1, 8)); graph[2].push_back(make_pair(3, 8)); graph[3].push_back(make_pair(2, 8)); graph[3].push_back(make_pair(4, 2)); graph[4].push_back(make_pair(3, 2)); graph[4].push_back(make_pair(5, 10)); graph[5].push_back(make_pair(4, 10)); graph[5].push_back(make_pair(6, 2)); graph[6].push_back(make_pair(5, 2)); int minCost = PRIM(graph, n); cout << "最小生成树的权值和:" << minCost << endl; system("pause"); return 0; }
参考输出:
最小生成树的权值和:28
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。