欢迎光临
我们一直在努力

20.Prim 算法:从起点出发,每次选最近的城市连成一张网

一、什么是 Prim 算法?

Prim 算法是一种最小生成树算法,由 Robert Prim 在 1957 年提出。它的核心思想是从一个起点出发,每次选择离当前连通分量最近的节点,将其加入连通分量,直到所有节点都连通,最终得到一棵总权重最小的生成树。

简单来说,Prim 算法就像 “从北京出发,逐步连通全国 34 个省份”:

  • 先从北京开始,找到离北京最近的天津,把它们连起来;
  • 再从当前连通的城市中,找到离它们最近的河北,连起来;
  • 重复这个过程,每次只连最近的城市,直到所有省份都连通;
  • 这样连出来的公路总长度最短,也就是最小生成树。

二、Prim 算法的核心步骤

Prim 算法的核心步骤可以分为以下几步:

  • 初始化:将起点加入连通分量,其他节点的距离设为无穷大;
  • 选择节点:从未加入连通分量的节点中,选择离连通分量最近的节点;
  • 更新距离:将该节点加入连通分量,更新其邻居节点到连通分量的距离;
  • 重复:继续选择下一个最近的节点,直到所有节点都加入连通分量。
  • 三、Prim 算法的代码实现

    1. Python 版本(直观易懂)

    import heapq

    def prim(graph, start):
    # 初始化距离:所有节点距离设为无穷大
    distances = {node: float('inf') for node in graph}
    distances[start] = 0

    # 优先队列:存储(距离,节点),距离小的优先
    priority_queue = []
    heapq.heappush(priority_queue, (0, start))

    # 记录是否已加入连通分量
    in_mst = {node: False for node in graph}
    # 记录路径
    path = {node: None for node in graph}

    total_weight = 0

    while priority_queue:
    current_distance, current_node = heapq.heappop(priority_queue)

    # 如果当前节点已加入连通分量,跳过
    if in_mst[current_node]:
    continue

    # 加入连通分量
    in_mst[current_node] = True
    total_weight += current_distance

    # 遍历邻居
    for neighbor, weight in graph[current_node].items():
    if not in_mst[neighbor] and weight < distances[neighbor]:
    distances[neighbor] = weight
    path[neighbor] = current_node
    heapq.heappush(priority_queue, (weight, neighbor))

    return total_weight, path

    # 测试:全国34个省份连通问题
    graph = {
    '北京': {'天津': 110, '河北': 263, '山西': 337},
    '天津': {'北京': 110, '河北': 269},
    '河北': {'北京': 263, '天津': 269, '山西': 174, '山东': 269, '河南': 360},
    '山西': {'北京': 337, '河北': 174, '内蒙古': 337, '河南': 360, '陕西': 435},
    '山东': {'河北': 269, '河南': 466, '安徽': 446},
    '河南': {'河北': 360, '山西': 360, '山东': 466, '陕西': 435, '湖北': 310, '安徽': 466},
    '安徽': {'山东': 446, '河南': 466, '江苏': 149, '湖北': 310, '浙江': 238},
    '江苏': {'安徽': 149, '上海': 165, '浙江': 238},
    '上海': {'江苏': 165, '浙江': 165},
    '浙江': {'安徽': 238, '江苏': 238, '上海': 165, '福建': 447, '江西': 261},
    '湖北': {'河南': 310, '安徽': 310, '江西': 261, '湖南': 287, '陕西': 435},
    '江西': {'湖北': 261, '浙江': 261, '福建': 447, '湖南': 287, '广东': 568},
    '湖南': {'湖北': 287, '江西': 287, '广东': 568, '广西': 411, '贵州': 373},
    '福建': {'浙江': 447, '江西': 447, '台湾': 250, '广东': 568},
    '台湾': {'福建': 250},
    '广东': {'江西': 568, '湖南': 568, '福建': 568, '广西': 411, '海南': 411, '香港': 65, '澳门': 65},
    '广西': {'湖南': 411, '广东': 411, '海南': 373, '贵州': 373, '云南': 461},
    '海南': {'广东': 411, '广西': 373},
    '香港': {'广东': 65},
    '澳门': {'广东': 65},
    '贵州': {'湖南': 373, '广西': 373, '云南': 461, '重庆': 325},
    '云南': {'广西': 461, '贵州': 461, '四川': 433, '西藏': 1248},
    '重庆': {'贵州': 325, '四川': 263, '陕西': 507},
    '四川': {'重庆': 263, '云南': 433, '西藏': 1248, '青海': 507, '甘肃': 507},
    '西藏': {'云南': 1248, '四川': 1248, '青海': 1441},
    '青海': {'四川': 507, '西藏': 1441, '甘肃': 507, '新疆': 1441},
    '甘肃': {'四川': 507, '青海': 507, '陕西': 507, '宁夏': 345, '新疆': 1441},
    '陕西': {'山西': 435, '河南': 435, '湖北': 435, '重庆': 507, '四川': 507, '甘肃': 507, '宁夏': 345},
    '宁夏': {'陕西': 345, '甘肃': 345, '内蒙古': 337},
    '内蒙古': {'山西': 337, '宁夏': 337, '辽宁': 605},
    '辽宁': {'内蒙古': 605, '吉林': 280, '河北': 605},
    '吉林': {'辽宁': 280, '黑龙江': 230},
    '黑龙江': {'吉林': 230},
    '新疆': {'甘肃': 1441, '青海': 1441}
    }

    total_weight, path = prim(graph, '北京')
    print("最小生成树总权重:", total_weight)
    print("路径:", path)

    2. C 语言版本(更贴近底层)

    #include <stdio.h>
    #include <stdlib.h>
    #include <limits.h>

    #define INF INT_MAX
    #define N 34 // 全国34个省份

    // 省份名称
    char* provinces[] = {
    "北京", "天津", "河北", "山西", "山东", "河南", "安徽", "江苏", "上海", "浙江",
    "湖北", "江西", "湖南", "福建", "台湾", "广东", "广西", "海南", "香港", "澳门",
    "贵州", "云南", "重庆", "四川", "西藏", "青海", "甘肃", "陕西", "宁夏", "内蒙古",
    "辽宁", "吉林", "黑龙江", "新疆"
    };

    // 图的邻接矩阵
    int graph[N][N] = {
    {0, 110, 263, 337, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {110, 0, 269, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {263, 269, 0, 174, 269, 360, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {337, INF, 174, 0, INF, 360, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 435, INF, 337, INF, INF, INF, INF},
    {INF, INF, 269, INF, 0, 466, 446, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, 360, 360, 466, 0, 466, INF, INF, INF, 310, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 435, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, 446, 466, 0, 149, INF, 238, 310, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, 149, 0, 165, 238, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, 165, 0, 165, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, 238, 238, 165, 0, INF, 261, INF, 447, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, 310, 310, INF, INF, INF, 0, 261, 287, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 435, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, 261, 261, 0, 287, 447, INF, 568, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 287, 287, 0, INF, INF, 568, 411, INF, INF, INF, 373, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, 447, INF, 447, INF, 0, 250, 568, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 250, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 568, 568, 568, INF, 0, 411, 411, 65, 65, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 411, INF, INF, 411, 0, 373, INF, INF, 373, 461, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 411, 373, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 65, INF, INF, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 65, INF, INF, INF, 0, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 373, INF, INF, INF, 373, INF, INF, INF, 0, 461, 325, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 461, INF, INF, INF, 461, 0, INF, 433, 1248, INF, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 325, INF, 0, 263, INF, INF, INF, 507, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 433, 263, 0, 1248, 507, 507, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 1248, INF, 1248, 0, 1441, INF, INF, INF, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 507, 1441, 0, 507, INF, INF, INF, INF, INF, INF, 1441},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 507, INF, 507, 0, 507, 345, INF, INF, INF, INF, 1441},
    {INF, INF, INF, INF, INF, 435, INF, INF, INF, INF, 435, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 507, INF, INF, INF, 507, 0, 345, INF, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 345, 345, 0, 337, INF, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 337, 0, 605, INF, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 605, 0, 280, INF, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 280, 0, 230, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 230, 0, INF},
    {INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, INF, 1441, 1441, INF, INF, INF, INF, INF, INF, 0}
    };

    // Prim算法
    void prim(int start) {
    int dist[N]; // 存储到连通分量的最短距离
    int in_mst[N]; // 标记是否已加入连通分量
    int path[N]; // 记录路径
    int total_weight = 0;

    // 初始化
    for (int i = 0; i < N; i++) {
    dist[i] = INF;
    in_mst[i] = 0;
    path[i] = -1;
    }
    dist[start] = 0;

    for (int count = 0; count < N; count++) {
    // 找到离连通分量最近的未加入节点
    int min_dist = INF, u = -1;
    for (int i = 0; i < N; i++) {
    if (!in_mst[i] && dist[i] < min_dist) {
    min_dist = dist[i];
    u = i;
    }
    }

    // 加入连通分量
    in_mst[u] = 1;
    total_weight += min_dist;

    // 更新邻居的距离
    for (int v = 0; v < N; v++) {
    if (!in_mst[v] && graph[u][v] != INF && graph[u][v] < dist[v]) {
    dist[v] = graph[u][v];
    path[v] = u;
    }
    }
    }

    // 打印结果
    printf("起点:%s\\n", provinces[start]);
    printf("最小生成树总权重:%d\\n", total_weight);
    printf("路径:\\n");
    for (int i = 0; i < N; i++) {
    if (path[i] != -1) {
    printf("%s -> %s,权重:%d\\n", provinces[path[i]], provinces[i], graph[path[i]][i]);
    }
    }
    }

    int main() {
    prim(0); // 从北京出发
    return 0;
    }

    四、Prim 算法的特点

    • 时间复杂度:O (n²)(朴素实现),O (m log n)(优先队列实现);
    • 空间复杂度:O (n),需要存储距离和访问标记;
    • 适用场景:稠密图,边数较多的图;
    • 从点出发:从一个起点出发,逐步扩展连通分量。

    五、Prim 算法的优化

    为了提高 Prim 算法的效率,可以进行以下优化:

  • 优先队列:使用优先队列存储节点,每次取出距离最小的节点,时间复杂度降为 O (m log n);
  • 斐波那契堆:使用斐波那契堆代替优先队列,时间复杂度降为 O (m + n log n);
  • 邻接表:使用邻接表代替邻接矩阵,减少空间使用,提高遍历效率。
  • 六、Prim 算法的实际应用场景

    Prim 算法是一种非常基础且重要的算法,常见场景包括:

  • 网络设计:设计公路网、电网、通信网,最小化建设成本;
  • 集群计算:将多个节点连接成一个集群,最小化通信成本;
  • 图像处理:图像分割,将相似的像素合并成一个区域;
  • 社交网络:找到最小的社交连接,将所有用户连通;
  • 全国交通网:像视频中展示的那样,从一个起点出发,逐步连通全国所有省份,最小化总里程。
  • 七、Prim 算法 vs Kruskal 算法

    Prim 算法和 Kruskal 算法是两种最常用的最小生成树算法,它们的区别如下:

    八、总结

    Prim 算法是一种最小生成树算法,它通过从一个起点出发,每次选择离当前连通分量最近的节点,将其加入连通分量,直到所有节点都连通,最终得到一棵总权重最小的生成树。

    Prim 算法的核心是优先选择最近的节点,适合稠密图,在网络设计、集群计算、图像处理等领域广泛应用。

    希望这篇文章能帮助你理解 Prim 算法的原理和实现!

     

    赞(0)
    未经允许不得转载:171主机测评 » 20.Prim 算法:从起点出发,每次选最近的城市连成一张网
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址