目录
最小生成树
最小生成树:在无向图中求一棵树(n-1条边,无环,连通所有点),而且这棵树的边权和最小
(ps:可能结果不止一种)
Prim
算法思路
Prim算法从一个顶点开始,每次选择与顶点权重最小的边。("点"为核心,适合稠密图,因为稠密图"边"多)
附带Prim模拟流程图 ,实际有两个解,具体可看文章最后,但两个解顶点权重总和相同,因此求出一种结果即可,保证实际应用绝对正确,大家也可学习作者是如何进行模拟,期待能够帮助你
(ps:最小生成树模拟量过于庞大,模拟了整整四页,有部分涂改,请见谅,但可保证改正后的一定正确,因此读者可主要看prim算法,另一个算法写的过于繁杂)
看在作者完整模拟的份上,可以求个赞、收藏与关注(比心)
代码
java
import java.util.Arrays;
public class PrimMST {
// 定义无穷大常量(用MAX_VALUE的一半防止加法溢出)
private static final int INF = Integer.MAX_VALUE / 2;
public static int prim(int[][] graph) {
int n = graph.length; // 图的顶点数量
int[] dist = new int[n]; // 存储各顶点到最小生成树(MST)的最小距离
boolean[] visited = new boolean[n]; // 标记顶点是否已加入MST
Arrays.fill(dist, INF); // 初始化所有距离为无穷大
dist[0] = 0; // 从顶点0开始,到自身的距离为0
int totalWeight = 0; // MST的总权重
// 需要选择n个顶点加入MST
for (int i = 0; i < n; i++) {
// 步骤1:找到未访问顶点中距离MST最近的顶点u,若找到,则添加进最小生成树(如i=4时,只剩V2与V5未访问过,并且此时dist = [0, 5, 1, 4, ,6 , 2],dist[1](V2)到最小生成树的距离为5,dist[4](V5)到最小生成树的距离为6,因此最近的顶点为V2,并添加进最小生成树中)
int u = -1; // 初始化为-1表示未找到
for (int j = 0; j < n; j++) {
// 如果顶点j未访问,且(u未改变 或 j的距离更小)
if (!visited[j] && (u == -1 || dist[j] < dist[u])) {
u = j; // 更新最近顶点
}
}
// 如果最近顶点的距离仍是INF,说明图不连通
if (dist[u] == INF) return -1;
visited[u] = true; // 标记u已加入MST
totalWeight += dist[u]; // 累加边权重
// 步骤2:更新u的邻接顶点到MST的最短距离(如从顶点V1开始时,到顶点V2的距离是6,但i=1的遍历中,找到最近顶点为V3时,此时最小生成树(V1与V3)到V2的最短距离更新为了5)
for (int v = 0; v < n; v++) {
// 如果v未访问,且u-v有边,且该边比当前记录的距离更小
if (!visited[v] && graph[u][v] != INF && graph[u][v] < dist[v]) {
dist[v] = graph[u][v]; // 更新距离
}
}
}
return totalWeight;
}
public static void main(String[] args) {
// 定义图的邻接矩阵(6个顶点)
int[][] graph = {
// 0 1 2 3 4 5
{0, 6, 1, 5, INF, INF}, // 顶点0的边
{6, 0, 5, INF, 3, INF}, // 顶点1的边
{1, 5, 0, 4, 6, 4}, // 顶点2的边
{5, INF, 4, 0, INF, 2}, // 顶点3的边
{INF, 3, 6, INF, 0, 6}, // 顶点4的边
{INF, INF, 4, 2, 6, 0} // 顶点5的边
};
System.out.println("Prim MST Weight: " + prim(graph));
}
}
模拟流程图
kruskal
加边之前,考虑"加边"的两个端点是不是已经连通,若已连通,则不加("边"为核心,适合稀疏图)
代码
java
import java.util.Arrays;
import java.util.Comparator;
public class KruskalMST {
// 定义边类(起点、终点、权重)
static class Edge {
int src, dest, weight;
public Edge(int src, int dest, int weight) {
this.src = src; // 起点
this.dest = dest; // 终点
this.weight = weight; // 边权重
}
}
// 定义并查集的子集类
static class Subset {
int parent; // 父节点
int rank; // 秩(用于优化)
}
// 查找根节点(带路径压缩)
private static int find(Subset[] subsets, int i) {
if (subsets[i].parent != i)
subsets[i].parent = find(subsets, subsets[i].parent); // 路径压缩
return subsets[i].parent;
}
// 合并两个集合(按秩合并)
private static void union(Subset[] subsets, int x, int y) {
int xroot = find(subsets, x);
int yroot = find(subsets, y);
// 将小秩树合并到大秩树下
if (subsets[xroot].rank < subsets[yroot].rank) {
subsets[xroot].parent = yroot;
} else if (subsets[xroot].rank > subsets[yroot].rank) {
subsets[yroot].parent = xroot;
} else {
subsets[yroot].parent = xroot;
subsets[xroot].rank++; // 秩相同时,合并后秩+1
}
}
public static int kruskal(int[][] graph) {
int V = graph.length; // 顶点数
Edge[] edges = new Edge[V*V]; // 边列表(最坏情况下全连接)
int edgeCount = 0; // 实际边数计数器
// 将邻接矩阵转换为边列表
for (int i = 0; i < V; i++) {
for (int j = i+1; j < V; j++) { // 只遍历上三角避免重复
if (graph[i][j] != INF) { // 如果存在边
edges[edgeCount++] = new Edge(i, j, graph[i][j]);
}
}
}
// 裁剪边数组到实际大小
edges = Arrays.copyOf(edges, edgeCount);
// 按边权重升序排序
Arrays.sort(edges, Comparator.comparingInt(o -> o.weight));
// 初始化并查集
Subset[] subsets = new Subset[V];
for (int v = 0; v < V; v++) {
subsets[v] = new Subset();
subsets[v].parent = v; // 初始时每个顶点是自己的父节点
subsets[v].rank = 0; // 初始秩为0
}
int totalWeight = 0; // MST总权重
int selectedEdges = 0; // 已选边数
int i = 0; // 当前处理的边索引
// 当已选边数 < V-1 且还有未处理的边时继续
while (selectedEdges < V - 1 && i < edges.length) {
Edge nextEdge = edges[i++]; // 取出当前最小权重边
int x = find(subsets, nextEdge.src); // 查找起点所在集合
int y = find(subsets, nextEdge.dest); // 查找终点所在集合
// 如果不在同一集合(不会形成环)
if (x != y) {
union(subsets, x, y); // 合并集合
totalWeight += nextEdge.weight; // 累加权重
selectedEdges++; // 已选边数+1
}
}
return totalWeight;
}
public static void main(String[] args) {
// 定义与Prim相同的图
int[][] graph = {
{0, 6, 1, 5, INF, INF},
{6, 0, 5, INF, 3, INF},
{1, 5, 0, 4, 6, 4},
{5, INF, 4, 0, INF, 2},
{INF, 3, 6, INF, 0, 6},
{INF, INF, 4, 2, 6, 0}
};
System.out.println("Kruskal MST Weight: " + kruskal(graph));
}
}
代码对应实现案例
左侧为原无向图,右侧为两种最小生成树的求解结果