克鲁斯卡尔(Kruskal)算法
修路问题
1、本质:最小生成树问题
2、最小生成树(MST),Minimum Cost Spanning Tree,给定一个带权的无向连通图,选取一棵生成树,使树上所有边上权的总和为最小
(1) N个顶点,一定有N-1条边
(2) 包含全部顶点
(3) N-1条边都在图中
克鲁斯卡尔算法查找最小生成树的方法
1、步骤
(1)将连通网中所有的边按照权值大小做升序排序,从权值最小的边开始选择,只要此边不和已选择的边一起构成环路,就可以选择它组成最小生成树
(2)对于 N 个顶点的连通网,挑选出 N-1 条符合条件的边,这些边组成的生成树就是最小生成树
2、Kruskal 算法从 n 个顶点 n 条边出发,不断扩充边,直到包括 n - 1 条边为止,适用于求边稀疏的最小生成树
3、举例
(1)连通网
(2)将连通网中的所有边按照权值大小做升序排序
(3)从 B-D 边开始挑选,由于尚未选择任何边组成最小生成树,且 B-D 自身不会构成环路,所以 B-D 边可以组成最小生成树
(4)D-T 边不会和已选 B-D 边构成环路,可以组成最小生成树
(5)A-C 边不会和已选 B-D、D-T 边构成环路,可以组成最小生成树
(6)C-D 边不会和已选 A-C、B-D、D-T 边构成环路,可以组成最小生成树
(7)C-B 边会和已选 C-D、B-D 边构成环路,因此不能组成最小生成树
(8)B-T 、A-B、S-A 三条边都会和已选 A-C、C-D、B-D、D-T 构成环路,都不能组成最小生成树。而 S-A 不会和已选边构成环路,可以组成最小生成树
(9)如图所示,对于一个包含 6 个顶点的连通网,已经选择了 5 条边,这些边组成的生成树就是最小生成树
克鲁斯卡尔(Kruskal)算法求最小生成树
1、基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路
2、步骤
(1)构造一个只含n个顶点的森林
(2)依权值从小到大从连通网中选择边加入到森林中,并使森林中不产生回路,直至森林变成一棵树为止
3、判断是否形成回路
(1)记录顶点在"最小生成树"中的终点
(2)将所有顶点按照从小到大的顺序排列,某个顶点的终点就是“在最小生成树中与它连通的最大顶点”
4、Kruskal 算法的计算一幅含有 V 个顶点和 E 条边的连通加权无向图的最小生成树
(1)所需的空间和 E 成正比
(2)所需的时间和 ElogE 成正比(最坏情况)
代码实现
public class Kruskal {
public static final int BLOCK = Integer.MAX_VALUE;//表示顶点之间不直接连通,顶点自身不连通
//Kruskal算法解决最小生成树问题
public static void main(String[] args) {
char[] vertexes = {'A', 'B', 'C', 'D', 'E', 'F', 'G'};
int[][] matrix = {
{BLOCK, 12, BLOCK, BLOCK, BLOCK, 16, 14},
{12, BLOCK, 10, BLOCK, BLOCK, 7, BLOCK},
{BLOCK, 10, BLOCK, 3, 5, 6, BLOCK},
{BLOCK, BLOCK, 3, BLOCK, 4, BLOCK, BLOCK},
{BLOCK, BLOCK, 5, 4, BLOCK, 2, 8},
{16, 7, 6, BLOCK, 2, BLOCK, 9},
{14, BLOCK, BLOCK, BLOCK, 8, 9, BLOCK}
};
Graph graph = new Graph(vertexes, matrix);
MinimumCostSpanningTree minimumCostSpanningTree = new MinimumCostSpanningTree();
minimumCostSpanningTree.kruskal(graph);
}
}
class MinimumCostSpanningTree {
//克鲁斯卡尔算法创建最小生成树
public void kruskal(Graph graph) {
int select = 0;//记录最小生成树的边的个数
int[] ends = new int[graph.numOfEdges];
Edge[] result = new Edge[graph.numOfEdges];//最小生成树
Edge[] edges = graph.getEdges();
graph.sortEdges(edges);//按照边的权值大小,从小到大排序
for (int i = 0; i < graph.numOfEdges; i++) {
//下标为i的边的两个顶点
int vertex1 = graph.getIndex(edges[i].start);
int vertex2 = graph.getIndex(edges[i].end);
int end1 = graph.getEnd(ends, vertex1);//vertex1的终点
int end2 = graph.getEnd(ends, vertex2);//vertex2的终点
if (end1 != end2) {//终点不同,不构成回路
ends[end1] = end2;
result[select++] = edges[i];
}
}
System.out.println("最小生成树");
for (int i = 0; i < select; i++) {
System.out.println(result[i]);
}
}
}
//带权无向图
class Graph {
public int numOfEdges;//边的个数
public char[] vertexes;//顶点数组
public int[][] matrix;//邻接矩阵,一维数组的元素代表边的权值
//创建图,为方便而直接赋值data,matrix,但拷贝data,matrix可以不破坏原数据
public Graph(char[] vertexes, int[][] matrix) {
this.vertexes = vertexes;
this.matrix = matrix;
for (int i = 0; i < vertexes.length; i++) {
for (int j = i + 1; j < vertexes.length; j++) {
if (this.matrix[i][j] != Kruskal.BLOCK) {
numOfEdges++;
}
}
}
}
//根据边的权值,从小到大冒泡排序
public void sortEdges(Edge[] edges) {
Edge temp;
for (int i = 0; i < edges.length - 1; i++) {
for (int j = 0; j < edges.length - 1 - i; j++) {
if (edges[j].weight > edges[j + 1].weight) {//½»»»
temp = edges[j];
edges[j] = edges[j + 1];
edges[j + 1] = temp;
}
}
}
}
//获取顶点的下标
public int getIndex(char vertex) {
for (int i = 0; i < vertexes.length; i++) {
if (vertexes[i] == vertex) {
return i;
}
}
return -1;
}
//获取图中的所有边,及边两边顶点
public Edge[] getEdges() {
int index = 0;
Edge[] edges = new Edge[numOfEdges];
for (int i = 0; i < vertexes.length; i++) {
for (int j = i + 1; j < vertexes.length; j++) {
if (matrix[i][j] != Kruskal.BLOCK) {
edges[index++] = new Edge(vertexes[i], vertexes[j], matrix[i][j]);
}
}
}
return edges;
}
//获取下标为i的顶点的终点
//ends:记录各顶点对应终点下标,该数组在生成最小生成树有动态变化
public int getEnd(int[] ends, int i) {
//从i自身所在的最小生成树,向根方向找终点
while (ends[i] != 0) {
i = ends[i];
}
return i;
}
}
//边
class Edge {
char start;//起点
char end;//终点
int weight;//权值
//构造器
public Edge(char start, char end, int weight) {
this.start = start;
this.end = end;
this.weight = weight;
}
@Override
public String toString() {
return "Edge{" +
"start=" + start +
", end=" + end +
", weight=" + weight +
'}';
}
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战