最小生成树
来源:互联网 发布:网络语音监控 编辑:程序博客网 时间:2024/06/16 18:43
最小生成树
在含有n个顶点的连通加权无向图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树。
普里姆(Prim)算法
算法流程
从单一顶点开始,普里姆算法按照以下步骤逐步扩大树中所含顶点的数目,直到遍及连通图的所有顶点。
- 输入:一个加权连通图,其中顶点集合为
V ,边集合为E ; - 初始化:
Vnew={x} ,其中x为集合V中的任一节点(起始点),Enew={} ; - 重复下列操作,直到
Vnew =V :- 在集合E中选取权值最小的边(u, v),其中u为集合
Vnew 中的元素,而v则是V 中没有加入Vnew 的顶点(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一); - 将v加入集合
Vnew 中,将(u, v)加入集合Enew 中;
- 在集合E中选取权值最小的边(u, v),其中u为集合
- 输出:使用集合
Vnew 和Enew 来描述所得到的最小生成树。
具体流程如下图所示:
算法实现(Java)
/** * @description 最小生成树之普里姆(Prim)算法 * 输入 --> 无向有权图和起始顶点下标 * 输出 --> 打印最小生成树边及其对应的权重 * @author GongchuangSu * @since 2016.04.13 * @version v1.0 */public class Prim { Edge[] result; // 最小生成树数组 WeightedGraph G; public void prim(WeightedGraph G, int s) { result = new Edge[G.size() - 1]; this.G = G; final int[] dist = new int[G.size()]; // 相关顶点间边的权值 final int[] pred = new int[G.size()]; // prim最小生成树结果数组 final boolean[] visited = new boolean[G.size()]; // 顶点访问标记数组 // 初始化 for (int i = 0; i < dist.length; i++) { dist[i] = Integer.MAX_VALUE; } dist[s] = 0; // 将起始点对应的最小权值设置为0 for (int i = 0; i < dist.length; i++) { final int next = minVertex(dist, visited); // 在dist中寻找未被访问点所对应的最小权值,并返回该顶点坐标 visited[next] = true; // 将该顶点设置为已访问 final int[] n = G.getNighbors(next); // 取得与该顶点相邻接的点 for (int j = 0; j < n.length; j++) { final int v = n[j]; final int d = G.getWeight(next, v); if (dist[v] > d && !visited[v]) { dist[v] = d; // 将更小权值保存至dist数组中 pred[v] = next; // 并将先前结点保存至pred数组中 } } } // 将结果保存至最小生成树数组 for (int i = 0; i < G.size() - 1; i++) { result[i] = new Edge(pred[i + 1], i + 1, G.getWeight(pred[i + 1], i + 1)); } // 打印结果 print(); } /** * 功能:在未遍历点中寻找距离最小的点,并返回该点位置 */ private static int minVertex(int[] dist, boolean[] v) { int x = Integer.MAX_VALUE; int y = -1; for (int i = 0; i < dist.length; i++) { if (!v[i] && dist[i] < x) { y = i; x = dist[i]; } } return y; } /** * 功能:边集结构体 */ private static class Edge { int source; int target; int weight; public Edge(int source, int target, int weight) { this.source = source; this.target = target; this.weight = weight; } } /** * 功能:打印结果 */ private void print() { System.out.print("Prim:\n"); for (int i = 0; i < G.size() - 1; i++) System.out.printf("(%d, %d) %d \n", result[i].source, result[i].target, result[i].weight); }}
源代码下载:普里姆(Prim)算法
时间复杂度
由算法代码中的循环嵌套可得知其算法时间复杂度为
克鲁斯卡尔(Kruskal)
算法流程
- 新建图
G ,G 中拥有原图中相同的节点,但没有边 - 将原图中所有的边按权值从小到大排序
- 从权值最小的边开始,如果这条边连接的两个节点于图
G 中不在同一个连通分量中,则添加这条边到图G 中(不构成环) - 重复3,直至图
G 中所有的节点都在同一个连通分量中
具体流程如下图所示:
为什么这条边一定属于最小树?
反证法:如果这条边不在最小生成树中,它连接的两个连通分量最终还是要连起来的,通过其他的连法,那么另一种连法与这条边一定构成了环,而环中一定有一条权值大于这条边的边,用这条边将其替换掉,图仍旧保持连通,但总权值减小了。也就是说,如果不选取这条边,最后构成的生成树的总权值一定不会是最小的。
算法实现(Java)
/** * @description 最小生成树之克鲁斯卡尔(Kruskal)算法 * 输入 --> 无向有权图 * 输出 --> 打印最小生成树边及其对应的权重 * @author GongchuangSu * @since 2016.04.13 * @version v1.0 */public class Kruskal { int vlen; // 顶点个数 int elen; // 边个数 Edge[] edges; // 边集数组 Edge[] result; // 最小生成树数组 int[] parent; // 用来判断边与边是否形成环路 WeightedGraph G; public void kruskal(WeightedGraph G){ vlen = G.size(); elen = G.getEdgNum(); edges = new Edge[elen]; result = new Edge[vlen-1]; parent = new int[vlen]; this.G = G; edges = getEdges(); // 获取图中所有的边 sortEdges(edges); // 对边进行排序 // 初始化数组值为0 for(int i = 0; i < vlen; i++){ parent[i] = 0; } // 循环每一条边 int index = 0; for(int i = 0; i < elen; i++){ int n = getEnd(parent, edges[i].source); int m = getEnd(parent, edges[i].target); // 如果n与m不相等,说明此边没有与现有生成树形成环路 if(n != m){ parent[n] = m; result[index++] = new Edge(edges[i].source, edges[i].target, edges[i].weight); } } // 打印结果 print(); } /** * 功能:边集结构体 */ private static class Edge{ int source; int target; int weight; public Edge(int source, int target, int weight){ this.source = source; this.target = target; this.weight = weight; } } /** * 功能:获取图中所有的边 */ private Edge[] getEdges(){ int index = 0; Edge[] edges = new Edge[elen]; for(int i = 0; i < vlen; i++){ for(int j = i + 1; j < vlen; j++) if(G.getWeight(i, j) != 0) edges[index++] = new Edge(i, j, G.getWeight(i, j)); } return edges; } /** * 功能:将边按照权值大小由小到大进行排序 */ private void sortEdges(Edge[] edges){ for(int i = 0; i < elen; i++) for(int j = i + 1; j < elen; j++) if(edges[i].weight > edges[j].weight){ Edge temp = edges[i]; edges[i] = edges[j]; edges[j] = temp; } } /** * 功能:查找顶点在该最小树中的终点下标 */ private int getEnd(int[] parent, int i) { while( parent[i] > 0 ) i = parent[i]; return i; } /** * 功能:打印结果 */ private void print() { System.out.print("Kruskal:\n"); for(int i = 0; i < vlen - 1; i++) System.out.printf("(%d, %d) %d \n",result[i].source, result[i].target, result[i].weight); }}
源代码下载:克鲁斯卡尔(Kruskal)算法
时间复杂度
由算法代码中的循环嵌套可得知其算法时间复杂度为
0 0
- 最小比例 最小生成树
- 最小生成树&&次最小生成树
- 最小生成生成树计数
- 树+最小生成树
- 最小生成树
- 最小生成树 MST
- 最小生成树Kruskal
- kruskal 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树 MST
- 最小生成树问题
- 最小生成树
- 最小生成树
- 最小生成树
- 最小生成树
- 热点板块黑马
- 乘积最大(动规例题)
- Unity3d SendMessage 简单用法笔记
- Linux/Windows 环境下openGL开发环境问题集锦
- [LeetCode]LRU Cache
- 最小生成树
- 【C++】C++对象内存模型简介
- iOS Masonry的使用详解
- 【NOIP2015模拟11.3】备用钥匙
- HDU 3625 Examining the Rooms (第一类斯特灵数,组合数学)
- 安卓010 Content Provider
- 发现的一个视频数据集
- [杂记]国内某两家IT公司面试经验
- itertools的使用