day55 图论章节刷题Part07([53.寻宝]prim算法、kruskal算法)

前言:使用最小生成树的方法解决将所有节点连接起来所需的最小路径问题。

prim算法

Prim算法是一种贪心算法,从任意一个顶点开始构建最小生成树。每次选择当前已加入生成树的顶点中,距离最近的尚未加入生成树的顶点,直到所有顶点都被加入生成树。

适用场景

  • 稠密图:Prim算法在稠密图(边数接近 n2 )中表现较好,因为它的复杂度为O(n^2),其中 n 为节点数量。
  • 单源最短路径:Prim算法可以从任意一个顶点开始构建最小生成树,适合需要从特定顶点开始的情况。
代码实现

prim三部曲:

第一步,选距离生成树最近节点

第二步,最近节点加入生成树

第三步,更新非生成树节点到生成树的距离(即更新minDist数组)

minDist数组 用来记录 每一个节点距离最小生成树的最近距离。由于题目中说了边的权重最大为10000,这里将边的最大值初始化为10001。

代码如下:

java 复制代码
import java.util.*;
public class Main{
    public static void main (String[] args) {
        Scanner scan=new Scanner(System.in);
        int v=scan.nextInt();
        int e=scan.nextInt();
        int[][] grid=new int[v+1][v+1];
        //初始化距离为1001
        for(int i=0;i<=v;i++){
            Arrays.fill(grid[i],10001);
        }
        //根据输入的边的权值建立地图
        for(int i=0;i<e;i++){
            int s=scan.nextInt();
            int t=scan.nextInt();
            int k=scan.nextInt();
            grid[s][t]=k;
            grid[t][s]=k;
        }
        //初始化最小距离为10001
        int[] minDist=new int[v+1];
        Arrays.fill(minDist,1001);
        //建立数组判断节点是否在树中
        boolean[] isInTree=new boolean[v+1];
        //先选择第一个节点加入树中
        minDist[1]=0;
        //外部循环,遍历每一个节点
        for(int i=1;i<=v;i++){
            int minVal=Integer.MAX_VALUE;
            int cur=-1;
            //找到不在树中且距离最近的节点
            for(int j=1;j<=v;j++){
                if(!isInTree[j] && minDist[j]<minVal){
                    minVal=minDist[j];
                    cur=j;
                }
            }
            //将当前节点加入树中
            isInTree[cur]=true;
            //更新节点到数的距离,主要更新与新加入的节点相连的节点
            for(int j=1;j<=v;j++){
                if(!isInTree[j] && grid[cur][j]<minDist[j]){
                    minDist[j]=grid[cur][j];
                }
            }
        }
        //prim算法的循环结束,计算路径总和
        int result=0;
        for(int i=2;i<=v;i++){
            result+=minDist[i];
        }
        System.out.println(result);
    }
}

注意:外部循环是为了确保每个节点都被遍历到。第一个内部循环是找到距离最小生成树最近的节点;第二个内部循环是更新minDist。

kruskal算法

Kruskal算法也是一种贪心算法,但它是从全局角度出发,先将所有边按权重从小到大排序,然后依次选择不形成环的边加入生成树,直到生成树包含所有顶点。

适用场景

  • 稀疏图:Kruskal算法在稀疏图(边数远小于 n2)中表现较好,因为它的复杂度为 nlogn,其中n 为边的数量。
  • 全局最优:Kruskal算法从全局角度出发,适合需要考虑所有边的情况。

代码实现

在代码中,我们可以使用并查集将两个节点加入同一个集合并判断两个节点是否在同一个集合。

时间复杂度:nlogn (快排) + logn (并查集) ,所以最后依然是 nlogn ,n为边的数量。

代码如下:

java 复制代码
import java.util.*;
//定义边的类
class Edge{
    int l,r,val;
    
    Edge(int l,int r,int val){
        this.l=l;
        this.r=r;
        this.val=val;
    }
}

public class Main{
    //题目中节点最多10000,所以初始化并查集的节点10001
    public static int n=10001;
    public static int[] father=new int[n];
    
    public static void init(){
        for(int i=0;i<n;i++){
            father[i]=i;
        }
    }
    
    public static int find(int u){
        return u==father[u]?u:(father[u]=find(father[u]));
    }
    
    public static void join(int u,int v){
        u=find(u);
        v=find(v);
        if(u==v) return;
        else father[v]=u;
    }
    
    public static void main (String[] args) {
        Scanner scan=new Scanner(System.in);
        int v=scan.nextInt();
        int e=scan.nextInt();
        
        List<Edge> edgeList=new LinkedList<>(); 
        for(int i=0;i<e;i++){
            int l=scan.nextInt();
            int r=scan.nextInt();
            int val=scan.nextInt();
            edgeList.add(new Edge(l,r,val));
        }
        
        //排序
        edgeList.sort(Comparator.comparingInt(edge -> edge.val));
        init();
        int result=0;
        for(Edge edge:edgeList){
            int x=find(edge.l);
            int y=find(edge.r);
            if(x!=y){
                result+=edge.val;
                join(x,y);
            }
        }
        System.out.println(result);
    }
}
相关推荐
九年义务漏网鲨鱼5 分钟前
【人脸伪造检测后门攻击】 Exploring Frequency Adversarial Attacks for Face Forgery Detection
论文阅读·python·算法·aigc
一个数据小开发6 分钟前
业务开发问题之ConcurrentHashMap
java·开发语言·高并发·map
_OLi_11 分钟前
力扣 LeetCode 977. 有序数组的平方(Day1:数组)
数据结构·算法·leetcode
会飞的架狗师22 分钟前
【Spring】Spring框架中有有哪些常见的设计模式
java·spring·设计模式
励志成为嵌入式工程师26 分钟前
c语言选择排序
c语言·算法·排序算法
風清掦30 分钟前
C/C++每日一练:编写一个查找子串的位置函数
c语言·c++·算法
Jakarta EE32 分钟前
在JPA和EJB中用乐观锁解决并发问题
java
花心蝴蝶.42 分钟前
并发编程中常见的锁策略
java·jvm·windows
A charmer1 小时前
算法每日双题精讲——滑动窗口(最大连续1的个数 III,将 x 减到 0 的最小操作数)
c++·算法·leetcode
A_cot1 小时前
一篇Spring Boot 笔记
java·spring boot·笔记·后端·mysql·spring·maven