prim算法VSkruskal算法

Prim 算法 vs Kruskal(库鲁斯卡尔)算法

Prim 算法和 Kruskal 算法都是用于求解 最小生成树(MST,Minimum Spanning Tree) 的经典算法,但它们在实现方式、适用场景和时间复杂度上有所不同。


对比表

对比项Prim 算法Kruskal(库鲁斯卡尔)算法
基本思想从一个顶点开始,每次选择当前最小的边扩展 MST按边权从小到大排序,每次选择不形成环的最小边加入 MST
算法类型贪心 + 逐步扩展贪心 + 并查集(Disjoint Set)
数据结构邻接矩阵(O(n²))或 最小堆 + 邻接表(O(m log n))并查集 + 边排序(O(m log m))
适用图稠密图(边数多)更优稀疏图(边数少)更优
选取方式逐步扩展到最近的未加入 MST 的点直接从所有边中选择最小的
是否支持负权边支持支持
是否保证连通必须连通才能运行不连通时可用于求最小生成森林
时间复杂度- O(n²)(邻接矩阵)
- O(m log n)(堆优化)
- O(m log m)(排序 + 并查集)

Prim 算法

思路

  1. 从任意一个顶点开始,把它加入 MST。
  2. 选择 与 MST 中的顶点相连权值最小 的边,将其对应的顶点加入 MST。
  3. 重复上述步骤,直到所有顶点都加入 MST。

实现方式

  • 邻接矩阵 + 朴素 Prim(O(n²)):适用于 稠密图(边数多)。
  • 邻接表 + 最小堆(O(m log n)):适用于 稀疏图(边数少)。

代码示例

#include <iostream>
#include <vector>
#include <climits>

using namespace std;

const int INF = INT_MAX;
const int V = 5;

int minKey(vector<int>& key, vector<bool>& inMST) {
    int min = INF, min_index;
    for (int v = 0; v < V; v++) {
        if (!inMST[v] && key[v] < min) {
            min = key[v], min_index = v;
        }
    }
    return min_index;
}

void primMST(vector<vector<int>>& graph) {
    vector<int> parent(V, -1), key(V, INF);
    vector<bool> inMST(V, false);
    
    key[0] = 0;

    for (int count = 0; count < V - 1; count++) {
        int u = minKey(key, inMST);
        inMST[u] = true;

        for (int v = 0; v < V; v++) {
            if (graph[u][v] && !inMST[v] && graph[u][v] < key[v]) {
                parent[v] = u, key[v] = graph[u][v];
            }
        }
    }
    
    cout << "Edge \tWeight\n";
    for (int i = 1; i < V; i++) {
        cout << parent[i] << " - " << i << " \t" << graph[i][parent[i]] << "\n";
    }
}

int main() {
    vector<vector<int>> graph = {
        {0, 2, 0, 6, 0},
        {2, 0, 3, 8, 5},
        {0, 3, 0, 0, 7},
        {6, 8, 0, 0, 9},
        {0, 5, 7, 9, 0}
    };

    primMST(graph);
    return 0;
}

Kruskal(库鲁斯卡尔)算法

思路

  1. 按权值排序所有边
  2. 逐步选择最小权重的边,如果不形成环,则加入 MST
  3. 使用并查集 维护已加入的边,防止形成环。
  4. 直到 MST 包含 n-1 条边。

实现方式

  • 排序 + 并查集(Union-Find)
  • O(m log m)(m 为边数,因排序复杂度较高)

代码示例

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

struct Edge {
    int u, v, weight;
    bool operator<(const Edge& other) const {
        return weight < other.weight;
    }
};

const int MAX_N = 1000;
int parent[MAX_N], rank_[MAX_N];

void init(int n) {
    for (int i = 0; i < n; i++) {
        parent[i] = i;
        rank_[i] = 1;
    }
}

int find(int x) {
    if (parent[x] != x) {
        parent[x] = find(parent[x]); // 路径压缩
    }
    return parent[x];
}

bool unite(int x, int y) {
    int rootX = find(x), rootY = find(y);
    if (rootX == rootY) return false;
    
    if (rank_[rootX] > rank_[rootY]) {
        parent[rootY] = rootX;
    } else if (rank_[rootX] < rank_[rootY]) {
        parent[rootX] = rootY;
    } else {
        parent[rootY] = rootX;
        rank_[rootX]++;
    }
    return true;
}

int kruskalMST(vector<Edge>& edges, int V) {
    sort(edges.begin(), edges.end());
    init(V);
    
    int mstWeight = 0, edgesUsed = 0;
    for (const Edge& e : edges) {
        if (unite(e.u, e.v)) {
            mstWeight += e.weight;
            edgesUsed++;
            if (edgesUsed == V - 1) break; // 生成 V-1 条边的最小生成树
        }
    }
    return mstWeight;
}

int main() {
    int V = 4;
    vector<Edge> edges = {
        {0, 1, 10}, {0, 2, 6}, {0, 3, 5}, {1, 3, 15}, {2, 3, 4}
    };

    cout << "Minimum Spanning Tree Weight: " << kruskalMST(edges, V) << endl;
    return 0;
}

Prim vs Kruskal:哪个更好?

情况推荐算法原因
稠密图(边多)PrimO(n²) 适用于邻接矩阵
稀疏图(边少)KruskalO(m log m) 适用于邻接表
边的数量 ≈ 顶点²Prim遍历顶点比遍历边快
边的数量 ≪ 顶点²Kruskal处理少量边更快
需要知道 MST 结构Prim可以直接记录树
需要快速求权值和Kruskal并查集查找更快

总结

  • Prim 适用于稠密图,邻接矩阵 O(n²) 或堆优化 O(m log n)。
  • Kruskal 适用于稀疏图,排序 + 并查集 O(m log m)。
  • 两者均支持负权边,不适用于负环(Dijkstra/Bellman-Ford 适合)。

在实际应用中,Kruskal 适用于道路网络、社交网络等图,Prim 适用于电网、计算机网络等稠密图。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值