如何使用C++中的最小生成树算法
最小生成树(Minimum Spanning Tree,MST)是图论中一个重要的概念,它表示连接一个无向连通图的所有顶点的边的子集,且这些边的权值之和最小。有多种算法可以用来求解最小生成树,如Prim算法和Kruskal算法。本文将介绍如何使用C++实现Prim算法和Kruskal算法,并给出具体的代码示例。
Prim算法是一种贪心算法,它从图的一个顶点开始,逐步选择与当前最小生成树连接的权值最小的边,并将该边加入到最小生成树中。以下是Prim算法的C++代码示例:
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
const int INF = 1e9;
int prim(vector<vector<pair<int, int>>>& graph) {
int n = graph.size(); // 图的顶点数
vector<bool> visited(n, false); // 标记顶点是否已访问
vector<int> dist(n, INF); // 记录顶点到最小生成树的最短距离
int minCost = 0; // 最小生成树的总权值
// 从第一个顶点开始构建最小生成树
dist[0] = 0;
// 使用优先队列保存当前距离最小的顶点和权值
priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
pq.push(make_pair(0, 0));
while (!pq.empty()) {
int u = pq.top().second; // 当前距离最小的顶点
pq.pop();
// 如果顶点已访问过,跳过
if (visited[u]) {
continue;
}
visited[u] = true; // 标记顶点为已访问
minCost += dist[u]; // 加入顶点到最小生成树的权值
// 对于顶点u的所有邻接顶点v
for (auto& edge : graph[u]) {
int v = edge.first;
int weight = edge.second;
// 如果顶点v未访问过,并且到顶点v的距离更小
if (!visited[v] && weight < dist[v]) {
dist[v] = weight;
pq.push(make_pair(dist[v], v));
}
}
}
return minCost;
}
int main() {
int n, m; // 顶点数和边数
cin >> n >> m;
vector<vector<pair<int, int>>> graph(n);
// 读取边的信息
for (int i = 0; i < m; ++i) {
int u, v, w; // 边的两个顶点及其权值
cin >> u >> v >> w;
--u; --v; // 顶点从0开始编号
graph[u].push_back(make_pair(v, w));
graph[v].push_back(make_pair(u, w));
}
int minCost = prim(graph);
cout << "最小生成树的权值之和为:" << minCost << endl;
return 0;
}
Kruskal算法是一种基于边的贪心算法,它从图的所有边中选择权值最小的边,并判断该边是否会形成环路。如果不会形成环路,则将该边加入到最小生成树中。以下是Kruskal算法的C++代码示例:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct Edge {
int u, v, weight; // 边的两个顶点及其权值
Edge(int u, int v, int weight) : u(u), v(v), weight(weight) {}
};
const int MAXN = 100; // 最大顶点数
int parent[MAXN]; // 并查集数组
bool compare(Edge a, Edge b) {
return a.weight < b.weight;
}
int findParent(int x) {
if (parent[x] == x) {
return x;
}
return parent[x] = findParent(parent[x]);
}
void unionSet(int x, int y) {
int xParent = findParent(x);
int yParent = findParent(y);
if (xParent != yParent) {
parent[yParent] = xParent;
}
}
int kruskal(vector<Edge>& edges, int n) {
sort(edges.begin(), edges.end(), compare);
int minCost = 0; // 最小生成树的总权值
for (int i = 0; i < n; ++i) {
parent[i] = i; // 初始化并查集数组
}
for (auto& edge : edges) {
int u = edge.u;
int v = edge.v;
int weight = edge.weight;
// 如果顶点u和顶点v不属于同一个连通分量,则将该边加入到最小生成树中
if (findParent(u) != findParent(v)) {
unionSet(u, v);
minCost += weight;
}
}
return minCost;
}
int main() {
int n, m; // 顶点数和边数
cin >> n >> m;
vector<Edge> edges;
// 读取边的信息
for (int i = 0; i < m; ++i) {
int u, v, w; // 边的两个顶点及其权值
cin >> u >> v >> w;
edges.push_back(Edge(u, v, w));
}
int minCost = kruskal(edges, n);
cout << "最小生成树的权值之和为:" << minCost << endl;
return 0;
}
通过以上代码示例,我们可以在C++中使用Prim算法和Kruskal算法求解最小生成树的问题。在实际应用中,可以根据具体情况选择合适的算法来解决问题。这些算法的时间复杂度分别为O(ElogV)和O(ElogE),其中V为顶点数,E为边数。因此,它们在处理大规模图的情况下也能够得到较好的效果。