最小生成树问题:算法分析 Java 实现
一、简介
1. 什么是最小生成树
将一个有权图中的 所以顶点 都连接起来,并保证连接的边的 总权重最小,即最小生成树(mini spanning tree)问题。
例如,电子电路设计中,将所有组件的针脚连接在一起,且希望所使用的连线长度最短。
2. 图示
如上图(这里借用的是《算法导论》一书中的图)所示,每条边上的数字表示权重。我们使用阴影边连接了所有的顶点,并保证了其总权重是最小的。
注意最小生成树可能并不是唯一的,例如上图中我们就可以将 (b, c) 边换成 (a, h) 边。
二、算法分析
1. 怎么求解
解决最小生成树的问题通常有两种解法:Kruskal 算法和 Prim 算法。它们都属于 贪婪算法,即每次总是寻找局部最优解。下面我们以 Kruskal 算法为例分析和求解该问题。
2. Kruskal 算法
第一步,我们找出 权重最短 的边,并将边的顶点合并到一颗树中,例如 (g, h);
第二步,在剩余边中继续找出 权重最短 的边,并将边的顶点合并到一颗树中,例如 (c, i);
重复第二步,直到所有的顶点都合并到同一颗树中。
注意,如果某条边的两个顶点已经在同一颗树中了,则跳过该边,因为加入该边将导致闭环(它的两个顶点已经在同一颗树中连接了,没必要再加这条边了)。
3. 过程图解
根据上述过程,我们始终找寻当前满足要求且权重最小的边:
三、代码实现
好了,理论说了这么多看着也乏味,关键是代码要怎么写呢?
1. Edge 类
我们算法最后返回的结果其实就是一个 “边” 的集合。我们很容易想到我们需要一个类来表示图的边,它应该包含两个顶点和权重这些信息,且之后我们需要根据边的权重从小到大排序,所以 Edge 类还应该实现 Comparable 接口。
public class Edge implements Comparable<Edge> {private Vertex start;private Vertex end;private int weight; // 权重public Edge(Vertex start, Vertex end, int weight) {this.start = start;this.end = end;this.weight = weight;}public Vertex getStart() {return start;}public Vertex getEnd() {return end;}@Overridepublic int compareTo(Edge other) {return this.weight - other.weight;}
}
2. Vertex 类
上面 Edge 类里有两个顶点,这个顶点类当然也是需要的。由于算法之后需要判断两个顶点是否在同一个树中,那么最简单的方式就是判断顶点目前所在的树的根结点是否相同即可。
所以我们需要通过 Vertex 类找到树的根结点,可以创建一个 TreeNode 类表示树的结点,然后 Vertex 类继承 TreeNode 类,因为顶点可以看作就是树中的一个叶子结点。
public class Vertex extends TreeNode {private char value; // 顶点的值public Vertex(char value) {this.value = value;}public char getValue() {return value;}public TreeNode getRoot() {TreeNode root = this;while (root.getParent() != null) {root = root.getParent();}return root;}public void setRoot(TreeNode treeNode) {getRoot().setParent(treeNode);}}
其父类为:
public class TreeNode {protected TreeNode parent;public TreeNode getParent() {return parent;}public void setParent(TreeNode parent) {this.parent = parent;}}
3. 场景类
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;public class Main {public static void main(String[] args) {List<Edge> edges = getTestData(); // 获取测试数据List<Edge> result = miniSpanningTree(edges); // 得到最小生成树printEdges(result); // 打印最小生成树的边}public static List<Edge> miniSpanningTree(List<Edge> edges) {ArrayList<Edge> result = new ArrayList<>();Collections.sort(edges); // 根据边权重从小到大排序for (Edge edge : edges) {Vertex u = edge.getStart();Vertex v = edge.getEnd();// 如果 u 和 v 已经在同一颗树里则跳过if (u.getRoot() == v.getRoot()) {continue;}result.add(edge);// 将 u 和 v 放在同一颗树里// 合并两个树最直接的办法就是使用一个新的根结点,然后连接两个子树TreeNode newRoot = new TreeNode();u.setRoot(newRoot);v.setRoot(newRoot);}return result;}public static List<Edge> getTestData() {ArrayList<Edge> list = new ArrayList<>();Vertex[] vertexes = new Vertex[9];for (int i = 0; i < vertexes.length; i++) {// 'a' to 'i'vertexes[i] = new Vertex((char) (i + 97));}list.add(new Edge(vertexes[0], vertexes[1], 4)); // a-blist.add(new Edge(vertexes[0], vertexes[7], 8)); // a-hlist.add(new Edge(vertexes[1], vertexes[2], 8)); // b-clist.add(new Edge(vertexes[1], vertexes[7], 11)); // b-hlist.add(new Edge(vertexes[2], vertexes[3], 7)); // c-dlist.add(new Edge(vertexes[2], vertexes[5], 4)); // c-flist.add(new Edge(vertexes[2], vertexes[8], 2)); // c-ilist.add(new Edge(vertexes[3], vertexes[4], 9)); // d-elist.add(new Edge(vertexes[3], vertexes[5], 14)); // d-flist.add(new Edge(vertexes[4], vertexes[5], 10)); // e-flist.add(new Edge(vertexes[5], vertexes[6], 2)); // f-glist.add(new Edge(vertexes[6], vertexes[7], 1)); // g-hlist.add(new Edge(vertexes[6], vertexes[8], 6)); // g-ilist.add(new Edge(vertexes[7], vertexes[8], 7)); // h-ireturn list;}public static void printEdges(List<Edge> edges) {for (int i = 0; i < edges.size(); i++) {Edge edge = edges.get(i);System.out.println("(" + edge.getStart().getValue() + ", " + edge.getEnd().getValue() + ")");}}}
4. 执行结果
(g, h)
(c, i)
(f, g)
(a, b)
(c, f)
(c, d)
(a, h)
(d, e)
省的大家往上翻了,最后这里也贴一下图:
最小生成树问题:算法分析 Java 实现相关推荐
- 最小生成树 kruskal_使用Kruskal算法求解Java最小生成树问题
最小生成树 kruskal In Electronic Circuit we often required less wiring to connect pins together. We can m ...
- 实验1 最小生成树问题【Kruskal+Prim】
1.贪心算法思想 贪心算法的基本思想是找出整体当中每个小的局部的最优解,并且将所有的这些局部最优解合起来形成整体上的一个最优解.因此能够使用贪心算法的问题必须满足下面的两个性质: 1.整体的最优解可以 ...
- 数据结构与算法-Prim算法解析与解决修路最小生成树问题
文章目录 简介 Prim算法 最小生成树 应用场景 问题描述 思路分析 代码实现 简介 Prim算法 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索 ...
- 最小生成树问题的算法笔记
最小生成树问题 前提 在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此边的权重,若存在 T 为 E 的子集且为无循环图,使得 ...
- 《LeetCode力扣练习》第64题 最小路径和 Java
<LeetCode力扣练习>第64题 最小路径和 Java 一.资源 题目: 给定一个包含非负整数的 m x n 网格 grid ,请找出一条从左上角到右下角的路径,使得路径上的数字总和为 ...
- 使用贪心算法解决最小生成树问题。
使用贪心算法解决最小生成树问题. #include<iostream> #include<algorithm> using namespace std; const int M ...
- 【算法】Kruskal算法(解决最小生成树问题) 含代码实现
Kruskal算法和Prim算法一样,都是求最小生成树问题的流行算法. 算法思想: Kruskal算法按照边的权值的顺序从小到大查看一遍,如果不产生圈或者重边,就把当前这条边加入到生成树中. 算法的正 ...
- Python小白的数学建模课-18.最小生成树问题
Python小白的数学建模课-18.最小生成树问题 最小生成树(MST)是图论中的基本问题,具有广泛的实际应用,在数学建模中也经常出现. 路线设计.道路规划.官网布局.公交路线.网络设计,都可以转化为 ...
- 数学建模之图论——图与网络模型(二)(最小生成树问题、最大流问题)
建议先看上一篇基本概念篇 https://blog.csdn.net/weixin_45755332/article/details/106899147 最小生成树 基本概念和方法 树:没有圈的连通图 ...
- 【数据结构笔记29】最小生成树问题:Prim算法与Kruskal算法
本次笔记内容: 8.1.1 Prim算法 8.1.2 Kruskal算法 文章目录 最小生成树问题 什么是最小生成树(Minimum Spanning Tree) 贪心算法 Prim算法 Kruska ...
最新文章
- 信息理论基础 周炯槃 常迥
- ZIP 也能边下载边解压?流式解压技术揭秘!
- 当你打开的程序在任务栏出现两个一样的怎么办?
- Matlab基本函数-hidden函数,matlab中hidden off有什么用?
- 【学术相关】普通在读奶爸博士生的一年总结!
- nuget pack
- linux服务器之查看内存使用情况
- LeetCode(28): 实现strStr()
- Python functool module
- NRF24L01使用外部中断读取数据的问题
- 【声学基础】概述——传播
- CleanCodeHandbook Chapter 5: Bit Manipulation(33-34)
- Vim 编辑器底端 [noeol], [dos] 的含义
- 【细节实现题】LeetCode 56. Merge Intervals
- 实战:配置内网DNS实现内部域名解析
- javascript遍历对象属性和方法
- 在IDEA中使用Maven详细教程
- ie 远程控制计算机,通过IE浏览器连接“远程桌面”方法详解
- 白话 P-value 这个再通俗不过了~
- mysql 不会丢失数据吗_讨论MySQL丢失数据的几种情况
热门文章
- 基于Unity3D 的Vuforia SDK开发基础教程
- 微信开发获取access_token(凭证)接口的详解
- Android—Surface,BufferQueue
- Winserver2016安装Exchange2016总结(一堆坑)
- qtcpsocket断开_关于QSocket的释放的一个需要注意的情况(必须先断开连接)
- 信用卡债务属于共同债务吗
- Linux下Moudle工具的介绍与使用
- 蚁群算法 c语言,蚁群算法(C语言实现)
- mysql database alias_mysql小结——基础篇
- 小姜杂谈:屏幕分辨率一次讲清楚