Prim算法

问题描述

  求解连通图的最小生成树问题

输入格式

  输入的第一行包含 N N N M M M两个整数。其中, N N N为连通图中点的数量, M M M为连通图中边的数量。所有点的编号从 0 0 0 N − 1 N-1 N1。随后有M行数据,一条边的数据占用一行,每行包含三个数据,依次为该边连接的两个顶点编号和该边的权重。始终至少有一种方法能产生最小生成树。

输出格式

  输出最小生成树的权重之和。

输入样例

6 10
0 1 4
0 4 1
0 5 2
1 2 1
1 5 3
2 3 6
2 5 5
3 4 5
3 5 4
4 5 3

输出样例

11

解题思路

测试代码

普通

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main(){
	struct node{///定义邻接表中的节点
	    size_t id;
	    int weight;
	    node(size_t id,int weight):id(id),weight(weight){}
	};
    size_t number,road;
    cin >> number >> road;
    vector<vector<node> > adjacency(number);///建立邻接表
    for(size_t i=0;i<road;i++){
        size_t left,right;
        int weight;
        cin >> left >> right >> weight;
        adjacency[left].push_back(node(right,weight));///向邻接表中放入边
        adjacency[right].push_back(node(left,weight));///向邻接表中放入边
    }
    vector<bool> mark(number,false);///标记已经访问过的点
    vector<int> table(number,INT_MAX);///记录出发点到各点的距离初始化为无穷大
    table[0] = 0;///出发点到出发点距离为零
    size_t next = 0;///初始化下一次访问的点为出发点
    int weight = 0;///初始化到下一次访问的点的距离为零
    do{
        for(node temp:adjacency[next]){///枚举当前访问点有向连接的各点
            if((!mark[temp.id])&&(temp.weight<table[temp.id])){
                table[temp.id] = temp.weight;///通过该点到其它点距离是否可以更近
            }
        }
        mark[next] = true;///标记该点已经访问过
        weight = UINT_MAX;///初始化到下一次访问点的距离为无穷大
        for(size_t i=0;i<number;i++){
            if(!mark[i]&&table[i]<weight){///选取未访问过的、到该点最小距离的点
                weight = table[i];///记录到该点的距离
                next = i;///记录该点的标号
            }
        }
    }while(weight<INT_MAX);///已经访问过该点所在连通图的所有点
    size_t sum = 0;
    for(size_t i=0;i<table.size();i++){
        sum += table[i];
    }
    cout << sum << endl;
    return 0;
}

优先队列优化

#include <bits/stdc++.h>
using namespace std;
void prim(const vector<vector<pair<int, int> > > &adj, int sta, vector<int> &tab){
    vector<bool> mas(adj.size(), false);
    tab.resize(adj.size(), INT_MAX);
    tab[sta] = 0;
    priority_queue<pair<int, int>, vector<pair<int, int> >, greater<pair<int, int> > > que;
    que.push({tab[sta], sta});
    while(!que.empty()){
        pair<int, int> nex = que.top();
        que.pop();
        if(mas[nex.second]){
            continue;
        }
        mas[nex.second] = true;
        for(auto &edg: adj[nex.second]){
            if(!mas[edg.second] && tab[edg.second]>edg.first){
                tab[edg.second] = edg.first;
                que.push({tab[edg.second], edg.second});
            }
        }
    }
}
int main(){
    int n, k;
    cin >> n >> k;
    vector<vector<pair<int, int> > > a(n);///建立邻接表
    for(int i=0; i<k; i++){
        int l, r, v;
        cin >> l >> r >> v;
        a[l].push_back({v, r});///向邻接表中放入边
        a[r].push_back({v, l});///向邻接表中放入边
    }
    vector<int> t;
    prim(a, 0, t);
    copy(t.begin(), t.end(), ostream_iterator<int>(cout, " "));
    cout << endl;
    size_t s = 0;
    for(int i: t){
        s += i;
    }
    cout << s << endl;
    return 0;
}
### Prim算法与Kruskal算法的比较 Prim算法和Kruskal算法都是用于求解加权连通图中最小生成树的经典算法。尽管它们的目标相同,但在实现方式、时间复杂度、空间复杂度以及适用场景等方面存在显著差异。 #### 时间复杂度 - **Prim算法**:在最基础的形式下,Prim算法的时间复杂度为 $O(n^2)$,其中 $n$ 表示顶点的数量。通过使用更高效的数据结构如二叉堆优化后,时间复杂度可以降低至 $O(E\log V)$,这里 $E$ 是边的数量,$V$ 是顶点的数量[^1]。 - **Kruskal算法**:Kruskal算法的时间复杂度主要受到排序所有边的影响,通常为 $O(E\log E)$ 或者等价于 $O(E\log V)$,因为边的数量最多可达 $V(V-1)/2$(对于完全图)[^1]。 #### 空间复杂度 - **Prim算法**:其空间复杂度主要取决于顶点数量,大约为 $O(V)$,因为它需要维护一个包含所有顶点的信息的数据结构来跟踪哪些顶点已经被加入到最小生成树中。 - **Kruskal算法**:其空间复杂度则与边数相关,约为 $O(E)$,主要用于存储所有的边及其权重信息。 #### 实现难度 - **Prim算法**:通常认为Prim算法比Kruskal算法更容易实现,尤其是当使用邻接矩阵作为数据结构时。它依赖于优先队列来选择下一个最近的顶点加入到已有的树中。 - **Kruskal算法**:相比之下,Kruskal算法的实现稍微复杂一些,因为它不仅需要对所有边按权重进行排序,还需要一种机制(如并查集)来检测和避免形成环路。 #### 适用场景 - **Prim算法**:更适合处理边稠密的图,即边的数量接近于顶点数量平方的情况。在这种情况下,Prim算法的性能优势更为明显[^3]。 - **Kruskal算法**:对于稀疏图而言,即边的数量远小于顶点数量平方的情况下,Kruskal算法的表现更加出色。由于只需要对所有边进行一次排序,因此在处理大规模稀疏图时效率更高。 综上所述,虽然两种算法都能有效地找到最小生成树,但根据具体的应用场景选择合适的算法是非常重要的。如果图是稠密的,那么Prim算法可能是更好的选择;而对于稀疏图,则推荐使用Kruskal算法。 ```python # 示例代码 - Kruskal算法的基本框架 class UnionFind: def __init__(self, size): self.parent = list(range(size)) def find(self, x): if self.parent[x] != x: self.parent[x] = self.find(self.parent[x]) return self.parent[x] def union(self, x, y): rootX = self.find(x) rootY = self.find(y) if rootX == rootY: return False self.parent[rootY] = rootX return True def kruskal(n, edges): uf = UnionFind(n) res = [] for u, v, weight in sorted(edges, key=lambda x: x[2]): if uf.union(u, v): res.append((u, v, weight)) if len(res) == n - 1: break return res ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值