0%

最小生成树算法

前言

最小生成树:一个包含n个顶点的无向连通图中,有一颗边权最小的生成树将所有顶点连通。

下面介绍Prim算法和Kruskal算法。

定义:$n$为顶点数,$m$为边数。

Prim算法

1.适用情况:适用于稠密图,$n^2$与$m$为一个级别。

2.时间复杂度:$O(n^2)$

3.图存储方式:邻接矩阵

4.算法描述:Prim算法与Dijkstra算法相似,设集合$S$为已连通的顶点,$dist$为点$i$到集合中的点的最短距离。

  a.初始时,任意选择一个顶点加入到集合$S$,用这个点更新与之相连的点的距离。

  b.选择不在$S$中距离最短的点,将该点加入到集合$S$,用这个点更新与之相连的点的距离。

  c.重复b,直到集合$S$包含所有点。

5.代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static int prim(){
Arrays.fill(dist, INF);//初始时,dist为无穷大

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[t] > dist[j]))t = j;
}

if(i != 0 && dist[t] == INF)return INF;//若该图为连通图,不会存在dist[t]=INF,若存在,说明该图不连通,没有最小生成树

st[t] = true;//将该点加入集合

if(i != 0)res += dist[t];//更新权重

for(int j = 1; j <= n; j++){
dist[j] = Math.min(dist[j], g[t][j]);//用该点去更新与之相连的点的距离
}
}
return res;
}

Kruskal算法

1.适用情况:适用于稀疏图,$n$与$m$为一个级别。

2.时间复杂度:$O(mlogm)$

3.图存储方式:存端点$(a, b)$和边权$w$

4.算法描述:

  a.将所有边按权重从小到大排序,生成树的边集为$S$。

  b.遍历每条边$(a, b)$,若$a$、$b$不连通,则将连通,$(a, b)$加入到$S$。

5.代码实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
class Main{
public static int N = 100010, n, m, M = 200010;
public static int[] p = new int[N];
public static Edge[] edge;//不能是Edge[] edge = new Edge[M],后面没有创建对象。

public static int find(int x){
if(p[x] != x)p[x] = find(p[x]);
return p[x];
}

public static int kruskal(){
//Kruskal算法
Arrays.sort(edge);//将所有边按权重从小到大排序

//初始化并查集
for(int i = 1; i <= n; i++)p[i] = i;

int res = 0, cnt = 0;//res记录最小生成树的树边权重之和, cnt记录当前加入的边数
//从小到大枚举所有边
for(int i = 0; i < m; i++){
int a = edge[i].a, b = edge[i].b, w = edge[i].w;

a = find(a);
b = find(b);
if(a != b){//判断两个节点是否连通,两个节点不连通
p[a] = b; //将两个集合合并
res += w;
cnt++;
}
}
return cnt < n - 1 ? -1 : res;
}

public static void main(String[] args){
Scanner sc = new Scanner(new BufferedInputStream(System.in));
n = sc.nextInt();
m = sc.nextInt();
edge = new Edge[m];
for(int i = 0; i < m; i++){
int a = sc.nextInt(), b = sc.nextInt(), w = sc.nextInt();
edge[i] = new Edge(a, b, w);
}
int res = kruskal();
if(res == -1)System.out.println("impossible"); //说明图不连通
else System.out.print(res);
}

static class Edge implements Comparable<Edge>{
int a, b, w;
public Edge(int a, int b, int w){
this.a = a;
this.b = b;
this.w = w;
}

public int compareTo(Edge o){
return this.w-o.w;
}
}
}