目录

  • 1.概述
  • 2.代码实现
    • 2.1.并查集
    • 2.2.邻接矩阵存储图
    • 2.3.邻接表存储图
    • 2.4.测试代码
  • 3.应用

本文参考:
《数据结构教程》第 5 版 李春葆 主编

1.概述

(1)在一给定的无向图 G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边,而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得联通所有结点的 w(T) 最小,则此 T 为 G 的最小生成树 (minimal spanning tree)

(2)克鲁斯卡尔 (Kruskal) 算法是一种按权值的递增次序选择合适的边来构造最小生成树的方法。假设 G = (V, E) 是一个具有 n 个顶点的带权连通无向图,T = (U, TE) 是 G 的最小生成树,则构造最小生成树的步骤如下:

  • 置 U 的初值为 V(即包含有 G 中的全部顶点),TE 的初值为空集(即图 T 中的每一个顶点都构成一个分量);
  • 将图 G 中的边按权值从小到大的顺序依次选取,若选取的边未使生成树 T 形成回路,则加入 TE,否则将其舍弃,直到 TE 中包含 (n - 1) 条边为止;

(3)例如,对带权连通无向图 G 使用克鲁斯卡尔 (Kruskal) 算法构造最小生成树的过程如下:

2.代码实现

2.1.并查集

在使用克鲁斯卡尔 (Kruskal) 算法来构造最小生成树时需要判断选择的边是否使树 T 形成回路,并还涉及到连通分量的合并,因此这里选择使用并查集来解决这个问题。有关并查集的具体知识可查看【数据结构】并查集这篇文章,并查集的代码实现如下:

//并查集
class UnionFind {//记录连通分量(树)的个数private int count;//节点 x 的根节点是 root[x]private int[] root;//构造函数public UnionFind(int n) {//初始时每个节点都是一个连通分量this.count = n;root = new int[n];//初始时每个节点的根节点都是其自己,即每棵树中只有一个节点for (int i = 0; i < n; i++) {root[i] = i;}}//将 p 和 q 连通public void union(int p, int q) {int rootP = find(p);int rootQ = find(q);if (rootP == rootQ) {// p 和 q 的根节点相同,它们本就是连通的,直接返回即可return;} else {root[rootQ] = rootP;// 两个连通分量合并成一个连通分量count--;}}//判断 p 和 q 是否互相连通,即判断 p 和 q 是否在同一颗树中public boolean isConnected(int p, int q) {int rootP = find(p);int rootQ = find(q);//如果 p 和 q 的根节点相同,则说明它们在同一颗树中,即它们是连通的return rootP == rootQ;}//查找节点 x 的根节点public int find(int x) {if (root[x] != x) {root[x] = find(root[x]);}return root[x];}//返回连通分量(树)的个数public int getCount() {return count;}
}

2.2.邻接矩阵存储图

class Solution {/*** @param1: 邻接矩阵*             adjMatrix[i][j] = 0 表示节点 i 和 j 之间没有边直接相连;*          adjMatrix[i][j] = weight > 0 表示节点 i 和 j 之间的边的权值;* @return: Kruskal 算法依次选择的边权值以及该边连接的两个节点* @description: 使用 Kruskal 算法得到最小生成树*/public List<int[]> kruskal(int[][] adjMatrix) {//图的节点数int n = adjMatrix.length;// edges 保存所有边及权重,int[] 中存储 {节点 i, 节点 j, i 和 j 之间的边的权值}List<int[]> edges = new ArrayList<>();for (int i = 0; i < n; i++) {for (int j = i + 1; j < n; j++) {if (adjMatrix[i][j] != 0) {edges.add(new int[]{i, j, adjMatrix[i][j]});}}}//将边按照权重进行升序排序Collections.sort(edges, Comparator.comparingInt(a -> a[2]));//最小生成树中所有边的权值之和int weightSum = 0;//保存 Kruskal 算法中依次选择的边权值以及该边连接的两个节点List<int[]> infos = new ArrayList<>();UnionFind uf = new UnionFind(n);//依次遍历排序后的每一条边for (int[] edge : edges) {int u = edge[0];int v = edge[1];int weight = edge[2];//选中的边会产生环,则不能将其加入最小生成树中if (uf.isConnected(u, v)) {continue;}//如果选中的边不会产生环,则它属于最小生成树weightSum += weight;//将节点 u 和 v 进行连通uf.union(u, v);infos.add(new int[]{u, v, weight});}System.out.println("最小生成树中边的权值之和:" + weightSum);return infos;}
}

2.3.邻接表存储图

class Solution {/*** @param1: 邻接表,List<int[]> nodes = adjList[i] 是一个 list,其中:*          nodes.get(j)[0] 表示与节点 i 相邻的第 j 个节点的编号;*          nodes.get(j)[1] 表示节点 i 与节点 nodes.get(j)[0] 之间的边权值;* @param1: 图的节点数* @return: kruskal 算法依次选择的边权值以及该边连接的两个节点* @description: 使用 kruskal 算法得到最小生成树*/public static List<int[]> kruskal(List<int[]>[] adjList, int n) {// edges 保存所有边及权重,int[] 中存储 {节点 i, 节点 j, i 和 j 之间的边的权值}List<int[]> edges = new ArrayList<>();for (int i = 0; i < adjList.length; i++) {// nodes 存储与节点 i 相邻的节点信息List<int[]> nodes = adjList[i];//遍历每个与节点 i 相邻的节点for (int[] node : nodes) {//节点 i 与 节点 v 相邻int v = node[0];// weight 为 i、v 之间的边的权值int weight = node[1];edges.add(new int[]{i, v, weight});}}//将边按照权重进行升序排序Collections.sort(edges, Comparator.comparingInt(a -> a[2]));//最小生成树中所有边的权值之和int weightSum = 0;//保存 Kruskal 算法中依次选择的边权值以及该边连接的两个节点List<int[]> infos = new ArrayList<>();UnionFind uf = new UnionFind(n);//依次遍历排序后的每一条边for (int[] edge : edges) {int u = edge[0];int v = edge[1];int weight = edge[2];//选中的边会产生环,则不能将其加入最小生成树中if (uf.isConnected(u, v)) {continue;}//如果选中的边不会产生环,则它属于最小生成树weightSum += weight;//将节点 u 和 v 进行连通uf.union(u, v);infos.add(new int[]{u, v, weight});}System.out.println("最小生成树中边的权值之和:" + weightSum);return infos;}
}

2.4.测试代码

public static void main(String[] args) {//邻接矩阵int[][] adjMatrix = {{0, 9, 0, 0, 0, 1, 0},{9, 0, 4, 0, 0, 0, 3},{0, 4, 0, 2, 0, 0, 0},{0, 0, 2, 0, 6, 0, 5},{0, 0, 0, 6, 0, 8, 7},{1, 0, 0, 0, 8, 0, 0},{0, 3, 0, 5, 7, 0, 0}};List<int[]> nodes1 = kruskal(adjMatrix);for (int[] node : nodes1) {System.out.println(node[0] + " "+ node[1] + " " + node[2]);}//邻接表int n = 7;List<int[]>[] adjList = new ArrayList[n];for (int i = 0; i < n; i++) {adjList[i] = new ArrayList<>();}adjList[0].add(new int[]{1, 9});adjList[0].add(new int[]{5, 1});adjList[1].add(new int[]{0, 9});adjList[1].add(new int[]{2, 4});adjList[1].add(new int[]{6, 3});adjList[2].add(new int[]{1, 4});adjList[2].add(new int[]{3, 2});adjList[3].add(new int[]{2, 2});adjList[3].add(new int[]{4, 6});adjList[3].add(new int[]{6, 5});adjList[4].add(new int[]{3, 6});adjList[4].add(new int[]{5, 8});adjList[4].add(new int[]{6, 7});adjList[5].add(new int[]{0, 1});adjList[5].add(new int[]{4, 8});adjList[6].add(new int[]{1, 3});adjList[6].add(new int[]{3, 5});adjList[6].add(new int[]{4, 7});//        List<int[]> nodes2 = kruskal(adjList, 7);
//        for (int[] node : nodes2) {//            System.out.println(node[0] + " "+ node[1] + " " + node[2]);
//        }
}

结果如下:

最小生成树中边的权值之和:24
0 5 1
2 3 2
1 6 3
1 2 4
3 4 6
4 5 8

3.应用

(1)求图的最小生成树许多实际应用,例如城市之间的交通工程造价最优问题就是一个最小生成树问题。

(2)大家可以去 LeetCode 上找相关的最小生成树的题目来练习,或者也可以直接查看LeetCode算法刷题目录 (Java)这篇文章中的最小生成树章节。如果大家发现文章中的错误之处,可在评论区中指出。

【算法】克鲁斯卡尔 (Kruskal) 算法相关推荐

  1. 算法之克鲁斯卡尔(Kruskal)算法

    克鲁斯卡尔(Kruskal)算法 克鲁斯卡尔(Kruskal)算法,是用来求加权连通图的最小生成树的算法. 基本思想:按照权值从小到大的顺序选择n-1条边,并保证这n-1条边不构成回路 具体做法:首先 ...

  2. 算法:通过克鲁斯卡尔(Kruskal)算法,求出图的最小生成树

    之前我给大家分享过用普利姆(Prim)算法来求出图的最小生成树(点我去看看),今天我再给大家分享一个也是求图的最小生成树的克鲁斯卡尔(Kruskal)算法 克鲁斯卡尔(Kruskal)算法,就相当于先 ...

  3. 数据结构与算法(7-3)最小生成树(普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法)

    目录 一.最小生成树简介 二.普里姆算法(Prim) 1.原理 2.存储 2-1.图顶点和权: 2-3. 最小生成树: 3.Prim()函数 3-1.新顶点入树 3-2.保留最小权 3-3. 找到最小 ...

  4. 对下图所示的连通网络G,用克鲁斯卡尔(Kruskal)算法求G的最小生成树T,请写出在算法执行过程中,依次加入T的边集TE中的边。说明该算法的基本思想及贪心策略,并简要分析算法的时间复杂度

    对下图所示的连通网络G,用克鲁斯卡尔(Kruskal)算法求G的最小生成树T,请写出在算法执行过程中,依次加入T的边集TE中的 边.说明该算法的基本思想及贪心策略,并简要分析算法的时间复杂度

  5. 普里姆(Prim)算法和克鲁斯卡尔(Kruskal)算法

    图是一种基础又重要的数据结构,图的生成树是图的一个极小连通子图.最小生成树是无向连通网的所有生成树中边的权值之和最小的一棵生成树.求图的最小生成树可以牵引出很多经典的题目,例如在N个城市之间建立通讯网 ...

  6. Java实现之克鲁斯卡尔(Kruskal)算法

    一.问题引入 1.问题引入 1)某城市新增7个站点(A,B,C,D,E,F,G),现在需要修路把7个站点连通 2)各个站点的距离用边线表示(权),比如A-B距离12公里 3)问:如何修路保证各个站点都 ...

  7. 【数据结构与算法】克鲁斯卡尔(Kruskal)算法

    一,应用场景 公交站问题 1)某城市新增7个站点(A,B,C,D,E,F,G),现在需要修路把7个站点连通 2)各个站点的距离用边线表示(权),比如 A - B距离12公里 3)问:如何修路保证各个站 ...

  8. 普里姆算法(Prim)和克鲁斯卡尔(Kruskal)算法

    普里姆算法(Prim)和克鲁斯卡尔(Kruskal)算法 普里姆算法的基本思想: 取图中任意一个顶点 v 作为生成树的根,之后往生成树上添加新的顶点 w.添加顶点w的条件为:w 和已在生成树上的顶点v ...

  9. 【数据结构】克鲁斯卡尔(Kruskal)算法 —PK— 普里姆(Prim)算法

    目录 一.克鲁斯卡尔(Kruskal)算法 二.普里姆(Prim)算法 三.两个算法对比 求图的最小生成树的典型算法: 克鲁斯卡尔(Kruskal)算法 普里姆(Prim)算法 注:考虑问题的出发点相 ...

最新文章

  1. 显示浏览器窗口的高度和宽度
  2. redis高级-内存淘汰策略
  3. 源码安装的php如何启动脚本,PHP源码编译安装管理常用脚本
  4. mysql 优化rand_mysql优化--巧用rand(),with rollup,help__update2014.1.13
  5. 【转】使用oschina的git服务器
  6. 用Python中的tkinter模块作图
  7. linux程序内码,windows系统与linux系统的内码转换总结
  8. Liferay7 BPM门户开发之15: Liferay开发体系简介
  9. 记一次内网环境正向代理极光推送
  10. 前端开发和后端开发究竟有什么区别?详细介绍
  11. 蘑菇街测试开发实习生面经
  12. 《Django开发教程》1.2 在ubuntu上安装Django
  13. 好家伙,公司服务器直接热崩掉了!
  14. 人工蜂群算法(ABC算法)
  15. Android Compose 版本与 Kotlin 版本的兼容问题
  16. OpenCV-Python身份证信息识别
  17. VNC Viewer连接树莓派无法调整分辨率
  18. 线性方程组(五)- 线性方程组的解集
  19. 企业即时通讯软件选型的注意事项
  20. 非常有用的免费UI设计工具和资源

热门文章

  1. word怎么删除参考文献的横线_word2016怎么去掉引用参考文献中的横线
  2. 打怪小游戏 勇者打恶龙1.1
  3. 希尔顿集团大中华区第450家酒店开业;Gap在山东青岛开新店 | 美通企业日报
  4. 十大编程语言,每一个都不容易学,但每一个又很有用,黑客必备
  5. Python自动化运行合成大西瓜|附小游戏地址
  6. r语言做绘制精美pcoa图_R语言:Bary-Curtis PCoA
  7. 原型设计工具Axure RP9下载、中文语言操作说明(赠授权码)
  8. 九龙证券|长线资金整体加仓 青睐能源科技材料等板块
  9. On the Opportunities and Risks of Foundation Models- APPLICATIONS
  10. Java线程生命周期与状态切换