引言
在图论中,最小生成树(Minimum Spanning Tree, 简称MST)是一个连通无向图的生成树,它的所有边的权重之和最小。Prim算法是一种常用的求解最小生成树的贪心算法之一。本文将介绍Prim算法的基本原理和使用C++实现的实例代码。
Prim算法简介
Prim算法通过不断选择与生成树距离最小的顶点来逐步构建最小生成树。算法从一个起始顶点开始,每次选择与当前生成树最小的顶点,并将该顶点加入生成树中,直到生成树涵盖了图的所有顶点。
代码实现
pair模板介绍
pair是C++标准库中提供的一种模板类,用于存储两个值的有序对。它可以将两个不同类型的值组合在一起,并提供了方便的访问和操作方法。
在Prim算法的示例代码中,pair<int, int>
被用作边的表示,其中第一个int
表示顶点的索引,第二个int
表示边的权重。通过使用pair
,可以将这两个相关的值打包在一起,方便地进行传递和处理。
pair提供了以下常用的操作和功能:
1.pair<T1, T2> p(val1, val2) 创建一个pair对象,其中T1是第一个值的类型,T2是第二个值的类型,val1和val2是要存储的实际值。
2.p.first和p.second: 分别访问pair对象中的第一个值和第二个值。
3.make_pair(val1, val2): 创建一个pair对象,与pair<T1, T2>(val1, val2)的效果相同,用于更方便地创建pair对象。
pair演示
#include <iostream>
#include <utility>using namespace std;
int main() {
pair<int, string> p(42, "Hello"); // 创建一个pair对象
int value1 = p.first; // 访问pair中的值
string value2 = p.second;cout << "value1: " << value1 << endl;
cout << "value2: " << value2 << endl;
pair<double, char> q = make_pair(3.14, 'X'); // 使用make_pair创建pair对象
double value3 = q.first; // 访问pair中的值
char value4 = q.second;cout << "value3: " << value3 << endl;
cout << "value4: " << value4 << endl;return 0;
}
实现部分
typedef pair<int, int> Edge; //表示顶点和它的边
typedef vector<vector<Edge>> Graph;vector<int> prim(const Graph& graph, int start) {
int n = graph.size(); // 图中顶点的数量
const int INF = 1e9; // 定义一个足够大的常数表示无穷大,可以表示两顶点没有边
vector<int> dist(n, INF); // 记录顶点到生成树的最小距离
vector<bool> visited(n, false); // 记录顶点是否已加入生成树
vector<int> parent(n, -1); // 记录顶点的父节点
dist[start] = 0; // 起始顶点距离为0
在Prim算法中,通过记录顶点的父节点,我们可以构建最小生成树的结构,并跟踪从顶点到根节点的路径。这对于后续的操作和分析非常有用,例如确定最小生成树中的边、计算最小生成树的总权重、输出最小生成树的路径等。
// 自定义优先队列的比较函数,优先选择距离最小的顶点
auto cmp = [](const Edge& a, const Edge& b) {
return a.second > b.second;};
priority_queue<Edge, vector<Edge>, decltype(cmp)> pq(cmp);
pq.push(make_pair(start, 0));
使用优先队列帮我我们将边从小到大选择,极大的简化了代码的复杂度。
其中:
Edge
:指定元素类型为Edge
,即pair<int, int>
类型的对象。
vector<Edge>
:指定底层容器类型为vector<Edge>
,用于存储元素。
decltype(cmp)
:推导比较函数cmp
的类型,并将其作为参数传递给优先队列的模板参数。这里使用decltype(cmp)
关键字根据cmp
的类型推导出比较函数的类型。
pq(cmp)
:创建一个名为pq
的优先队列对象,并将比较函数cmp
作为参数传递给构造函数,用于定义元素之间的优先级。
while (!pq.empty()) {
int u = pq.top().first;
pq.pop();visited[u] = true;
for (const auto& edge : graph[u]) { // 遍历与顶点 u 相邻的边
int v = edge.first;
int weight = edge.second;if (!visited[v] && weight < dist[v]) {
dist[v] = weight;
parent[v] = u;
pq.push(make_pair(v, weight));
}
}
}
测试实例
#include <iostream>
#include <vector>
#include <queue>
#include <climits>
using namespace std;typedef pair<int, int> Edge; // 边的表示:(顶点, 权重)
typedef vector<vector<Edge>> Graph; // 图的邻接表表示vector<int> prim(const Graph& graph, int start) {
int n = graph.size(); // 图中顶点的数量
vector<int> dist(n, INT_MAX); // 记录顶点到生成树的最小距离
vector<bool> visited(n, false); // 记录顶点是否已加入生成树
vector<int> parent(n, -1); // 记录顶点的父节点
dist[start] = 0; // 起始顶点距离为0// 自定义优先队列的比较函数,优先选择距离最小的顶点
auto cmp = [](const Edge& a, const Edge& b) {
return a.second > b.second;};
priority_queue<Edge, vector<Edge>, decltype(cmp)> pq(cmp);
pq.push(make_pair(start, 0));while (!pq.empty()) {
int u = pq.top().first;
pq.pop();visited[u] = true;
// 遍历与顶点 u 相邻的边
for (const auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;if (!visited[v] && weight < dist[v]) {
dist[v] = weight;
parent[v] = u;
pq.push(make_pair(v, weight));
}
}
}return parent;
}int main() {
int n = 5; // 图中顶点的数量// 初始化图的邻接表表示
Graph graph(n);
graph[0].push_back(make_pair(1, 2));
graph[0].push_back(make_pair(3, 6));
graph[1].push_back(make_pair(0, 2));
graph[1].push_back(make_pair(2, 3));
graph[1].push_back(make_pair(3, 8));
graph[1].push_back(make_pair(4, 5));
graph[2].push_back(make_pair(1, 3));
graph[2].push_back(make_pair(4, 7));
graph[3].push_back(make_pair(0, 6));
graph[3].push_back(make_pair(1, 8));
graph[4].push_back(make_pair(1, 5));
graph[4].push_back(make_pair(2, 7));int start = 0; // 起始顶点
vector<int> parent = prim(graph, start);
// 输出最小生成树的边
for (int i = 1; i < n; i++) {
cout << parent[i] << " - " << i << endl;
}return 0;
}