最小生成树Prim算法详解(C++)

这篇具有很好参考价值的文章主要介绍了最小生成树Prim算法详解(C++)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Prim:

Prim算法是一种用于寻找加权无向图的最小生成树的贪心算法。它的基本思路是从图中任意一个点开始,选择与该点相邻的最小边,并将该边所连接的点加入到生成树的集合中。然后再从新加入的点出发,重复该过程直到所有的节点都被加入到生成树中。

邻接表:

#include <iostream>
#include <vector>
#include <queue>
#include <cstring>
using namespace std;

const int INF = 0x3f3f3f3f;   // 定义一个表示无穷大的值

vector<pair<int, int>> adj[10005];   // 邻接表,用于存储图的边信息
int dist[10005];    // 用于记录从起点到其他各个点的距离
bool vis[10005];    // 用于记录某个点是否已经加入生成树

int prim(int start) {
    memset(dist, INF, sizeof(dist));    // 初始化距离数组为无穷大
    memset(vis, false, sizeof(vis));    // 初始化所有点都未被访问过

    dist[start] = 0;    // 起点到自己的距离为0
    priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> pq;
    pq.push(make_pair(0, start));   // 将起点加入到优先队列中

    int ans = 0;    // 用于记录最小生成树的总权值

    while (!pq.empty()) {
        int u = pq.top().second;
        pq.pop();

        if (vis[u]) continue;    // 如果u已经被访问过,则跳过

        vis[u] = true;    // 将u标记为已访问

        for (int i = 0; i < adj[u].size(); i++) {
            int v = adj[u][i].first;
            int w = adj[u][i].second;

            if (!vis[v] && w < dist[v]) {
                dist[v] = w;
                pq.push(make_pair(dist[v], v));
            }
        }

        ans += dist[u];
    }

    return ans;
}

int main() {
    int n, m;
    cin >> n >> m;

    for (int i = 0; i < m; i++) {
        int u, v, w;
        cin >> u >> v >> w;

        adj[u].push_back(make_pair(v, w));
        adj[v].push_back(make_pair(u, w));    // 由于是无向图,所以要将另一条边也加入邻接表中
    }

    cout << prim(1) << endl;

    return 0;
}
  1. 初始化:首先需要初始化距离数组dist和标记数组vis。因为在Prim算法中,我们需要记录从起点到其他各个点的距离,同时还需要记录每个节点是否已经加入到生成树中。这里我们可以使用一个布尔数组vis来记录每个节点的状态,如果某个节点已经被加入到生成树中,那么vis[i]就为true,否则为false。距离数组dist用于记录从起点到每个节点的距离,这里我们将所有距离初始化为无穷大,表示起点到其他各个点的距离都还没有计算过。

  2. 将起点加入到优先队列中:在Prim算法中,我们需要从某个点开始,不断选择与该点相邻的最小边,并将该边所连接的点加入到生成树的集合中。因此,我们将起点加入到优先队列中,并将起点到自己的距离dist[start]设为0。这里需要使用一个小根堆priority_queue,来维护到各个节点的距离,以便每次选择最小的距离进行扩展。

  3. 不断扩展生成树:在优先队列中每次取出距离起点最近的点u,如果这个点已经被访问过了,则直接跳过。否则,将u标记为已经访问过,并将从u出发的所有边中,与还未加入到生成树中的节点中距离最小的那条边加入到生成树中,并将该节点加入到优先队列中。

  4. 计算最小生成树的权值:在Prim算法中,每次加入一条边都会产生一个权值,因此,需要用一个变量ans来记录最小生成树的权值,每次将新加入的边的权值累加到ans中。

  5. 返回最小生成树的权值:当优先队列为空时,表示所有节点都已经加入到生成树中了,此时可以返回ans作为最小生成树的权值。

总结:Prim算法的实现过程比较简单,主要就是使用一个优先队列来维护到各个节点的距离,并不断从优先队列中选择距离起点最近的点,然后将从该点出发的所有边中,与还未加入到生成树中的节点中距离最小的那条边加入到生成树中,不断重复这个过程直到所有节点都加入到生成树中为止。

邻接矩阵:

#include<bits/stdc++.h>
using namespace std;
#define inf 1e8 // 定义一个无穷大的常量

const int MAXN = 1005;

int graph[MAXN][MAXN]; // 邻接矩阵
int dis[MAXN]; // 存储顶点到生成树的距离
bool vis[MAXN]; // 标记顶点是否已经加入到生成树中

int Prim(int n) { // n为图的顶点数
    int ans = 0;
    memset(vis, false, sizeof(vis)); // 初始化标记数组为false
    memset(dis, inf, sizeof(dis)); // 初始化到生成树距离为无穷大
    dis[1] = 0; // 从任意一个点开始都可以
    for (int i = 1; i <= n; ++i) {
        int u = -1;
        for (int j = 1; j <= n; ++j) {
            if (!vis[j] && (u == -1 || dis[u] > dis[j])) { // 找到未加入生成树的距离最小的点
                u = j;
            }
        }
        vis[u] = true; // 将找到的点加入生成树
        ans += dis[u]; // 将距离加入到答案中
        for (int v = 1; v <= n; ++v) {
            if (!vis[v] && graph[u][v] < dis[v]) { // 更新未加入生成树的点到生成树的距离
                dis[v] = graph[u][v];
            }
        }
    }
    return ans;
}

int main() {
    int n, m;
    cin >> n >> m; // 输入图的顶点数和边数
    memset(graph, inf, sizeof(graph)); // 初始化邻接矩阵中的距离为无穷大
    for (int i = 1; i <= m; ++i) {
        int u, v, w;
        cin >> u >> v >> w; // 输入边的两个端点和边的权值
        graph[u][v] = graph[v][u] = w; // 更新邻接矩阵
    }
    int ans = Prim(n); // 计算最小生成树的权值和
    cout << ans << endl;
    return 0;
}

该代码使用了邻接矩阵来表示图,并使用了一个dis数组来存储每个点到生成树的距离,以及一个vis数组来标记每个点是否已经加入到生成树中。Prim算法的具体实现过程如下:

该算法需要对所有的n个点进行n次扫描,每次扫描都需要对剩余的n个点进行比较。而每次更新dis数组时,需要遍历所有的n个点。因此,该算法的时间复杂度为O(n^2)。

需要注意的是,该算法使用了一个dis数组来存储每个点到生成树的距离,而每次找到距离最小的点时,需要遍历所有未加入生成树的点。这种遍历未加入生成树的点的方式虽然看似效率低下,但实际上并不会影响算法的时间复杂度,因为在每次遍历时,只有一个点被加入到生成树中,因此下一次遍历时,该点就不再被考虑。因此,该算法的时间复杂度仍然为O(n^2)。

评价:

Prim算法的时间复杂度与实现方式有关。如果采用邻接矩阵的方式来表示图,那么Prim算法的时间复杂度就是O(V^2),其中V是图的顶点数。因为在邻接矩阵中,我们需要遍历每一个点,以确定该点是否已经加入到生成树中,并找到连接该点的最小边。这个过程的时间复杂度是O(V^2),因此Prim算法的总时间复杂度就是O(V^2)。

但是如果采用邻接表的方式来表示图,那么Prim算法的时间复杂度就是O(ElogV),其中E是图的边数,V是图的顶点数。因为在邻接表中,我们可以直接访问与某个点相邻的边,并找到连接该点的最小边,这个过程的时间复杂度是O(logV),因此Prim算法的总时间复杂度就是O(ElogV)。

  1. 初始化dis数组和vis数组。将dis数组中除了起点以外的所有元素都设为无穷大,将vis数组中所有元素都设为false。
  2. 将起点加入到生成树中,将其在vis数组中标记为true。
  3. 对于所有未加入生成树的点,更新它们到生成树的距离。
  4. 找到未加入生成树的距离最小的点u,将其加入到生成树中,将其在vis数组中标记为true,将dis[u]加入到答案中。
  5. 对于所有未加入生成树的点v,如果从u到v的边权值小于dis[v],则更新dis[v]的值为该边的权值。

因此,Prim算法的时间复杂度取决于图的表示方式。在邻接矩阵的情况下,Prim算法的时间复杂度是O(V^2),而在邻接表的情况下,Prim算法的时间复杂度是O(ElogV)。文章来源地址https://www.toymoban.com/news/detail-512904.html

到了这里,关于最小生成树Prim算法详解(C++)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Prim算法求最小生成树

    给定一张边带无权的无向图G = (V, E), n = |V|, m = |E|。由V中全部n个顶点和E中n - 1条边构成的无向连通子图被称为G的一课生成树。边的权值之和最小的生成树被称为无向图的最小生成树。 Prim算法总是维护最小生成树的一部分。最初,Prim算法仅确定1号节点属于最小生成树

    2024年02月12日
    浏览(43)
  • Prim算法实现最小生成树

    例如:要在n个城市之间铺设光缆,主要目标是要使这n个城市的任意两个之间都可以通信,但铺设光缆的费用很高,且各个城市之间铺设光缆的费用不同,因此另一个目标是要使铺设光缆的总费用最低。这就需要找到带权的最小生成树。 将图中所有顶点分为两类:树顶点(已

    2024年02月11日
    浏览(43)
  • 最小生成树——prim算法实现

    N个城市之间需要铺设一张交通网,使任意两个城市间都有交通路线直达,现已知各个城市之间铺设道路的经济成本,问该如何求算铺网的最低经济成本?为求算最低经济成本,可以假设N个城市就是连通网G的N个顶点,而求算最低成本问题可以转化为在N个城市间找到N-1条边,

    2024年02月08日
    浏览(38)
  • 最小生成树(Prim算法,Kruskal算法)

    (1)生成树: 如果在一个无向连通图不包含回路(连通图中不存在环),则为一个树 (2)最小生成树(minimal spanning tree): 在一个图所有生成树中,代价最小的生成树称为最小生成树 (3)生成树的代价: 在一个无向连通网中,生成树各边的权值之和称为该生成树的代价

    2024年02月08日
    浏览(44)
  • 最小生成树——普利姆(Prim)算法

    构成连通网的最小代价生成树称为最小生成树(Minimun Cost Spanning Tree). 最小生成树可以运用到生活中,假如你是一位工程师,需要为一个镇的九个村庄架设通信网络做设计,村庄位置大值如下图:  其中 V0~V8 是村庄,之间连线的数字表示村与村间的可通达的直线距离,比如

    2024年02月04日
    浏览(42)
  • 图的最小生成树-Prim算法

    目录 问题引入  程序设计  程序分析 本节文章 【问题描述】 编写程序,利用带权无向图的邻接矩阵存储,实现图的最小生成树Prim算法。 【输入形式】 输入图的顶点序列及图的边的情况。如样例所示。边的输入以输入-1,-1,-1,作为结束。 0,1,6 表示对应的顶点及边是:

    2024年02月08日
    浏览(53)
  • 最小生成树——Prim算法(详细图解)

    目录  最小生成树的概念   经典题目 prim算法简介  prim算法解析 (详细图解)  代码实现  代码实战 在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此的边权重,若存在 T 为 E 的子集(即)且为无循环图,使得的 w(T) 最小,则此 T 为 G 的

    2023年04月09日
    浏览(41)
  • 最小生成树—Kruskal算法和Prim算法

    连通图:在无向图中,若从顶点v1到顶点v2有路径,则称顶点v1与顶点v2是连通的。如果图中任 意一对顶点都是连通的,则称此图为连通图。 生成树:一个连通图的最小连通子图称作该图的生成树。有n个顶点的连通图的生成树有n个顶点 和n-1条边。 最小生成树:构成生成树的

    2024年02月05日
    浏览(46)
  • 最小生成树(Prim算法与Kruskal算法)

    一个连通图的生成树是一个极小的连通子图,它含有图中全部的n个顶点,但只有足以构成一棵树的n-1条边。我们把构造连通网的最小代价生成树称为最小生成树。 例如下图中①、②、③都是左侧图的生成树,但③是构造连通网的最小代价,所以③是该图的最小生成树。 P

    2024年02月05日
    浏览(58)
  • 最小(代价)生成树—Prim算法与Kruskal算法

    目录  一、最小生成树的特点 二、最小生成树算法  ① Prim(普里姆)算法 ②Kruskal(克鲁斯卡尔)算法  ③Prim算法与Kruskal算法对比 最小生成树是带权连通图G=(V,E)的生成树中边的权值之和最小的那棵生成树。它具有以下特点: 图G中各边权值互不相等时有唯一的最小生成树。图

    2024年02月01日
    浏览(38)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包