最小生成树

文章目录

  • 最小生成树
    • Prim(朴素版)
    • Krusal算法

最小生成树

最小生成树问题一般对应无向图

一般稠密图用朴素版的Prim算法,稀疏图用Kruskal算法

  • Prim算法
    • 朴素版(稠密图)On^2
    • 堆优化版(稀疏图)Omlogn
  • Kruskal算法 Omlogm

Prim(朴素版)

集合:指当前已经在连通块中的点

到集合的距离:指到集合中的最近的点的距离

  1. 把所有距离初始化成正无穷
  2. n次迭代,每次迭代时,找到不在集合中的距离最近的点t,把t加到集合中(st[ t ] = true),用t来更新其他点到集合的距离
public class Main{
    static final int N = 510;
    static int[][]g = new int[N][N];
    static boolean[]st = new boolean[N];
    static int[]dist = new int[N];
    static int M = 0x3f3f3f3f;
    static int n;
    static int m;
    static int prim() {
        Arrays.fill(dist,M);
        int res = 0;//记录最小生成树的权重之和
        for(int i = 0;i < n;i++) {
            int t = -1;
            for(int j = 1;j <= n;j++) {
                if(!st[j] && (t == -1 || dist[j] < dist[t])) t = j;
            }
            st[t] = true;
            if(i != 0 && dist[t] == M) return M;//距离集合最近的点到集合的距离还是M,说明这个点和集合不连通,不存在最小生成树
            if(i != 0) res += dist[t];//这里将找到的一个结果加进结果集需要在根据t更新其他点到集合的距离之前,为了防止自环边的影响
            for(int j = 1;j <= n;j++) dist[j] = Math.min(dist[j],g[t][j]);
        }
        return res;
    }
    
    public static void main(String[]args) {
        Scanner sc = new Scanner(System.in);
        n = sc.nextInt();
        m = sc.nextInt();
        for(int i = 1;i <= n;i++) {
            for(int j = 1;j <= n;j++) {
                g[i][j] = M;
            }
        }
        while(m --> 0) {
            int x = sc.nextInt();
            int y = sc.nextInt();
            int z = sc.nextInt();
            g[x][y] = g[y][x] = Math.min(g[x][y],z);
        }
        
        int r = prim();
        if(r == M) System.out.println("impossible");
        else System.out.println(r);
    }
}

Krusal算法

  1. 将所有边按权重从小到大排序(该算法的一个瓶颈)
  2. 枚举每条边ab和权重c,如果a,b不连通(不在同一个集合中),将这条边加入集合 // 使用并查集
import java.util.*;
class Edge implements Comparable{
    int a;
    int b;
    int w;
    public Edge(int a,int b,int w) {
        this.a = a;
        this.b = b;
        this.w = w;
    }
    @Override
    public int compareTo(Object o) {
        Edge e = (Edge)o;
        return this.w - e.w;
    }
}

public class Main {
    static final int N = 100010;
    static int[]p = new int[N];
    static int find(int x) {
        if(x != p[x]) p[x] = find(p[x]);
        return p[x];
    }
    
    public static void main(String[]args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt();
        for(int i = 1;i <= n;i++) p[i] = i;
        int m = sc.nextInt();
        Edge[]e = new Edge[m];
        for(int i = 0;i < m;i++) {
            int u = sc.nextInt();
            int v = sc.nextInt();
            int w = sc.nextInt();
            e[i] = new Edge(u,v,w);
        }
        Arrays.sort(e);//将所有边按权重从小到大排序
        int res = 0;
        int cnt = 0;
        for(int i = 0;i < m;i++) {//如果两点之间根本不连通(没有边),那么根本不会枚举不存在的边
            int a = e[i].a;
            int b = e[i].b;
            int w = e[i].w;
            a = find(a);
            b = find(b);
            if(a != b) {//若两者根父节点不同,则两点不在同一个集合中
                p[a] = b;//并查集合并集合
                res += w;
                cnt++;
            }
        }
        if(cnt < n - 1) System.out.println("impossible");
        else System.out.println(res);
    }
}

你可能感兴趣的:(总结,java,算法,开发语言)