《探索 Prim 算法:图论中最小生成树的构造方法》

前言

编程语言:Java, C++,Python,Js的朋友可以借助Chat GPT转换。
IDE: eclipse, /写算法不用IDEA
前面说了最小生成树的Kruskal算法, 本篇简单说一下Prim算法, 它同样是一种从图中构造最小生成树的算法。
本篇将对比Kruskal算法详解prim算法。
Prim-wiki详细:可以完全参考这篇, 只需要看我的代码即可

关于代码实现, 需要建图!, 提供邻接表和链式前向星。 有关邻接矩阵的实现可以借助chatgpt转换。还是以洛谷这道模板

Prim算法

Prim 与 Kruskal对比:引入

prim算法的使条件还是连通无向带权图。
Prim算法不如Kruskal算法常用。
Kruskal算法是通过遍历图中所有边的方式, 优先选择最小权值最小的边。 K算法中点唯一的作用是判断是否成环。 判断成环的方式通过并查集, 如果两个点是位于同一集合,那么就舍弃当前边。

算法思想

Prim算法中点很重要, 它是通过点的角度出发选择对应的边的, 直到选择的所有边涵盖了图中的所有顶点
Prim算法不必纠结证明, 因为它还是一种贪心策略, 局部最优 == 全局最优的结果。
需要借助两种数据结构: 集合, 小根堆(最小优先队列)
1. 初始化set集合和小根堆heap, 它们均为空容器。
2. 任意或者根据题意指定一个顶点作为开始点, 将点加入set里, 遍历该点的所有边加入到heap。

3. 从heap里弹出权值最小的边e, 通过边e去往另一个端点v。 此时, 有两种情况, 如果顶点v已经进入了set里, 那么说明边e不能添加进去,否则会成环; 如果顶点v不在set里, 那么边e可以添加到最小生成树, 同时端点v也要添加进set。
3. 重复步骤3, 如果当前图中所有顶点都被处理过, 那么heap迟早会成空堆, 因此空堆作为循环的终止条件。

以下摘自wiki
解释下图, 下图中选择D点作为初始点。
然后用颜色标记该点的所有边, 选择权值最小且符合条件不会成环的边。
下面它选择了D->A这条边, 同时A,D应该进入集合,下次出现这两个点的边应该舍去。
A点附近的所有边解放, 再和原先的D附近边选出最小的边。显然D->F满足条件, F点放进集合里, F点附近的边解放了。
现在可选的边{A->B:7,D->B:9,D->E:15,F->E:8,F->G:11}, 已经加入最小生成树的点{A, D, F}
优先选择权值最小的边A->B, B点进入集合。将B附近的边解放了, B->C:8, B->E:7
…如此,只要点处理完,最小生成树也就出来了。
注意, 上面描述省略了,将边重复加入的过程,比如D->B的先进入,但处理B点时, 会再次将B->D这组无向边重复添加进去, 虽然实际有set集合避免了重复处理, 但实际这个可以完全优化掉的。
在这里插入图片描述
后续部分请参考前言给出的wiki链接/

Java:邻接表实现

提交时需要将类名修改为Main, 删除包名
提交链接跟Kruskal算法的洛谷题一致: 最小生成树

package MST;

import java.io.*;
import java.util.ArrayList;
import java.util.PriorityQueue;
public class Code02_Prim {
   

    //Java中高效处理IO的写法
    public static void main(String[] args) throws IOException{
   
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer in = new StreamTokenizer(br);
        PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
        while(in.nextToken() != StreamTokenizer.TT_EOF){
   
            //n:图中顶点数
            int n = (int)in.nval;
            //邻接表方式进行建图。 int[]: * 2的数组, 0下标是边的下一个端点, 1下标是边的权值
            ArrayList<ArrayList<int[]>> graph = new ArrayList<>();
            //邻接表进行初始化
            for(int i=0;i<=n;i++){
   
                graph.add(new ArrayList<>());
            }
            in.nextToken();
            //m:边的数量
            int m = (int)in.nval;
            for(int i=0,u, v, w;i < m;i++){
   
                //读入起点,终点,权值 建立无向边。
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值