Prim算法:求解无向带权图的最小生成树

引言

  在图论中,最小生成树(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;
}

  • 32
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值