prim算法是解决最小生成树的经典算法,在算法竞赛中很常见。以下给出使用java实现prim算法的代码和具体过程。
java
import java.sql.Array;
import java.util.Arrays;
import java.util.Scanner;
public class Prim算法 {
public static void main(String[] args) {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt(),m = scan.nextInt();
//n,m分别表示节点数和边数
//默认是1-n个节点
int[][] arr = new int[n+1][n+1];
//输入节点相连的边和边上的权值
int a = 0,b = 0,v = 0;
for(int i = 0;i<m;i++) {
a = scan.nextInt();
b = scan.nextInt();
v = scan.nextInt();
arr[a][b] = v;
arr[b][a] = v;
}
//因为prim算法每次只找一个边,而一个边连接一个已访问节点和一个未访问节点
//所以每次记录一个节点就表示记录一条边
//找到所有已经标记的边中最小权值的边(节点)
//并将该节点的所有相邻节点记录
int[] log = new int[n+1];
Arrays.fill(log, 2000);
boolean[] flags = new boolean[n+1];
Arrays.fill(flags,false);
//初始化
int cur_point= 1;
log[cur_point] = 0;
//true表示已经标记了
flags[cur_point] = true;
int count = 0;
for(int i = 1;i<=n-1;i++) {
//将当前节点的相邻节点记录
for(int j = 1;j<=n;j++) {
if(arr[cur_point][j]!=0&&arr[cur_point][j]<log[j]) {
log[j] = arr[cur_point][j];
}
}
//找到log数组中的最小值
int temp = 0;
int min_num = 2000;
for(int j = 1;j<=n;j++) {
if(!flags[j]&&log[j]<min_num) {
min_num = log[j];
temp = j;
}
}
//处理单个节点
if(temp!=0) {
flags[temp] = true;
cur_point = temp;
}else {
break;
}
count+=min_num;
}
//还需判断当前图是否联通
for(int i = 1;i<=n;i++) {
if(!flags[i]) {
System.out.println("当前图不连通");
return;
}
}
System.out.println(count);
scan.close();
}
}
1.输入输出
输入:无向图的节点数、边数,以及每条边的两个端点和权值;
核心逻辑:从 1 号节点出发,逐步选择 "连接已访问节点集和未访问节点集" 的最小权值边,最终累加所有边的权值得到最小生成树总权值;
输出:若图连通,输出最小生成树总权值;若不连通,输出 "当前图不连通"。
2.相关变量
log数组:核心状态数组,log[j] 记录 "从已访问节点集到未访问节点j" 的最小权值边(初始值 2000 表示 "无穷大",代表暂时无连接);
flags数组:标记节点是否已加入最小生成树(true= 已加入);
cur_point:当前已加入生成树的 "最新节点"(初始为 1 号);
count:最终要输出的最小生成树总权值。
3.实现思路
最小生成树包含 n 个节点、n-1 条边,因此程序循环 n-1 次完成边的选取,核心分为两个步骤:
-
更新邻接节点权重:遍历当前已接入生成树节点的所有相邻节点,若节点间存在连通边,且该边权重小于log数组中记录的节点最短权重,则更新log数组,实时刷新未接入节点到生成树的最小距离。
-
选取最优接入节点:遍历所有未接入生成树的节点,筛选出log数组中权重最小的节点,该节点即为当前可接入生成树、开销最小的节点。将该节点标记为已接入,更新当前遍历节点,并累加边的权重,统计最小生成树总权重。若遍历无可用节点,说明图无法继续连通,直接终止迭代。
迭代结束后,遍历flags标记数组,检查所有节点是否全部接入最小生成树。若存在未标记节点,判定当前图不连通,无法生成最小生成树;若所有节点成功接入,则输出累加得到的最小生成树总权重。