普里姆(Prim)算法
修路问题
1、本质:最小生成树问题
2、最小生成树(MST),Minimum Cost Spanning Tree,给定一个带权的无向连通图,选取一棵生成树,使树上所有边上权的总和为最小
(1) N个顶点,一定有N-1条边
(2) 包含全部顶点
(3) N-1条边都在图中
普里姆算法查找最小生成树的过程
1、采用了贪心算法的思想
(1)对于包含 N 个顶点的连通网,普里姆算法每次从连通网中找出一个权值最小的边,这样的操作重复 N-1 次,由 N-1 条权值最小的边组成的生成树就是最小生成树
(2)Prim 算法从 1 个起点 0 条边出发,不断扩充顶点,直到包含所有顶点,适用于求边稠密的最小生成树
2、实现思路
(1)将连通网中的所有顶点分为两类(假设为 A 类和 B 类)。初始状态下,所有顶点位于 B 类
(2)选择任意一个顶点,将其从 B 类移动到 A 类
(3)从 B 类的所有顶点出发,找出一条连接着 A 类中的某个顶点且权值最小的边,将此边连接着的 A 类中的顶点移动到 B 类
(4)重复执行第 3 步,直至 B 类中的所有顶点全部移动到 A 类,恰好可以找到 N-1 条边
3、举例
(1)连通网
(2)将图中的所有顶点分为 A 类和 B 类,初始状态下,A = {},B = {A, B, C, D, S, T}
(3)从 B 类中任选一个顶点,假设选择 S 顶点,将其从 B 类移到 A 类,A = {S},B = {A, B, C, D, T}。从 A 类的 S 顶点出发,到达 B 类中顶点的边有 2 个,分别是 S-A 和 S-C,其中 S-A 边的权值最小,所以选择 S-A 边组成最小生成树,将 A 顶点从 B 类移到 A 类,A = {S, A},B = {B, C, D, T}
(4)从 A 类中的 S、A 顶点出发,到达 B 类中顶点的边有 3 个,分别是 S-C、A-C、A-B,其中 A-C 的权值最小,所以选择 A-C 组成最小生成树,将顶点 C 从 B 类移到 A 类,A = {S, A, C},B = {B, D, T}
(5)从 A 类中的 S、A、C 顶点出发,到达 B 类顶点的边有 S-C、A-B、C-B、C-D,其中 C-D 边的权值最小,所以选择 C-D 组成最小生成树,将顶点 D 从 B 类移到 A 类,A = {S, A, C, D},B = {B, T}
(6)从 A 类中的 S、A、C、D 顶点出发,到达 B 类顶点的边有 A-B、C-B、D-B、D-T,其中 D-B 和 D-T 的权值最小,任选其中的一个,例如选择 D-B 组成最小生成树,将顶点 B 从 B 类移到 A 类,A = {S, A, C, D, B},B = {T}
(7)从 A 类中的 S、A、C、D、B 顶点出发,到达 B 类顶点的边有 B-T、D-T,其中 D-T 的权值最小,选择 D-T 组成最小生成树,将顶点 T 从 B 类移到 A 类,A = {S, A, C, D, B, T},B = {}
(8)由于 B 类中的顶点全部移到了 A 类,因此 S-A、A-C、C-D、D-B、D-T 组成的是一个生成树,而且是一个最小生成树,它的总权值为 17
普利姆(Prim)算法求最小生成树
1、在包含 n 个顶点的连通图中,找出只有 (n - 1) 条边包含所有 n 个顶点的连通子图,即极小连通子图
2、步骤
(1) 设 G = (V, E) 是连通网,T = (U, D) 是最小生成树,V, U 是顶点集合,E, D 是边的集合
(2) 假设从顶点 a 开始构造最小生成树,则从集合 V 中取出顶点 a 放入集合 U 中,标记顶点 a 的 visited[a] = 1
(3) 从集合 U 的复数顶点发散,寻找与集合 V 中不构成回路且边的权值最小的顶点 c,假设在集合 U 中与顶点 c 相连的是顶点 b,将顶点 c 加入集合 U 中,将 b 与 c 的边加入集合 D 中,标记 visited[b] = 1
(4) 重复步骤(3),直到 U 与 V 相等,即所有顶点都被标记为访问过,此时 D 中有 n-1 条边
代码实现
public class Prim {
public static final int BLOCK = Integer.MAX_VALUE;//表示顶点之间不直接连通,顶点自身不连通
//Prim算法解决最小生成树问题
public static void main(String[] args) {
char[] data = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] matrix = new int[][]{
{BLOCK, 5, 7, BLOCK, BLOCK, BLOCK, 2},
{5, BLOCK, BLOCK, 9, BLOCK, BLOCK, 3},
{7, BLOCK, BLOCK, BLOCK, 8, BLOCK, BLOCK},
{BLOCK, 9, BLOCK, BLOCK, BLOCK, 4, BLOCK},
{BLOCK, BLOCK, 8, BLOCK, BLOCK, 5, 4},
{BLOCK, BLOCK, BLOCK, 4, 5, BLOCK, 6},
{2, 3, BLOCK, BLOCK, 4, 6, BLOCK}
};
Graph graph = new Graph(data, matrix);
MinimumCostSpanningTree mst = new MinimumCostSpanningTree();
mst.prim(graph, 0);
}
}
class MinimumCostSpanningTree {
//Prim算法创建最小生成树
public void prim(Graph graph, int start) {//传入图,开始节点
int[] visited = new int[graph.numOfVertexes];//标记结点是否被访问过,0:未访问,1:已访问
visited[start] = 1;
int vertex1 = -1;//已访问节点下标
int vertex2 = -1;//与vertex1直接连接的结点下标
int minMatrix = Prim.BLOCK;//记录遍历过程中边的最小权值,初始BLOCK
//i从1开始,产生graph.numOfVertexes - 1条边
for (int i = 1; i < graph.numOfVertexes; i++) {
//遍历结点
for (int j = 0; j < graph.numOfVertexes; j++) {
//判断结点j是否已访问
if (visited[j] == 1) {
//遍历结点j的直接连接结点k
for (int k = 0; k < graph.numOfVertexes; k++) {
//判断直接连接结点k是否未访问
if (visited[k] == 0 && graph.matrix[j][k] < minMatrix) {
minMatrix = graph.matrix[j][k];//取得当中边的最小权值
vertex1 = j;
vertex2 = k;
}
}
}
}
System.out.println("第" + i + "条边" + graph.data[vertex1] + graph.data[vertex2] + "权值" + minMatrix);
visited[vertex2] = 1;
minMatrix = Prim.BLOCK;
}
}
}
//带权无向图
class Graph {
int numOfVertexes;//结点个数
char[] data;//结点数据
int[][] matrix;//邻接矩阵,一维元素元素值为边的权值,BLOCK表示不连通
//创建图,为方便而直接赋值data,matrix,但拷贝data,matrix可以不破坏原数据
public Graph(char[] data, int[][] matrix) {
this.data = data;
this.numOfVertexes = data.length;
this.matrix = matrix;
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战