最小生成树原理及Kruskal算法的js实现
1. 生成树和最小生成树的概念
设图G(V,E)
连通,则
生成树:包含图G(V,E)
中的所有节点,及|V|-1
条边的连通图,一个图的生成树可以有多颗
最小生成树:最小权重生成树,在生成树的概念上加一个限制条件,即生成树的所有边的权值总和最小的树,最小生成树也可以有多颗
2. 求解最小生成树的通用方法
由于最小生成树包含图G的所有边,所以我们需要做的只是寻找最小生成树的边集A
设:边集A是图G的任意一颗最小生成树的边的子集,初始时A为空当A不等于G的某个最小生成树的所有边集M时循环以下步骤 找到一条属于M但不属于A的边,加入到A中
现在问题我们如何去寻找那条只属于M但不属于A的边
边v的寻找方法
当A为空时,图G(V,A)是一个有|V|个树的森林,当A中有n条边时,n<|V|-1,图G是一个有|V|-(n+1)个树的森林,我们需要寻找的边v的加入会导致图G中的森林数目减1边v是这样一条边
- 边v的两端的节点属于两颗不同的树
- 边v的权值是所有满足以上条件中权值最小的
3. Kruskal和Prim 算法
Kruskal
和Prim
算法是最小生成树常用的两种算法,这两种算法都是对上述通用方法的细化,不同之处就是对边v的寻找方法上有所差异,Kruskal
算法又叫做(边扩展)算法,适用于边稀疏的图,Prim
算法叫做(节点扩展算法),适用于边稠密的图
4. Kruskal算法
- 4.1. 概念
Kruskal算法的特点是上述A中的边可以属于多颗不同的树 - 4.2. 辅助函数
MakeSet(x)
MakeSet
操作创建一个包含|V|颗树的集合,每颗树只包含一个节点,我们要为每个节点x添加两个属性
var MakeSet = (function(){let set = new Set();return function(x) {x.parent = x;x.rank = 0;if(!set.has(x)) set.add(x);return set;}
})();
- 4.3. 辅助函数
Find(x)
找到并返回x节点所在的那颗树的根节点,用于判断两个节点是否在同一颗树中,即是否相交
function Find(x) {if (x.parent != x)x.parent = Find(x.parent);return x.parent;
}
- 4.4. 辅助函数
Union(u, v)
Union
函数旨在合并两个节点,应该将这里的合并和在图G中的连通区分开,我们通过不断调用union
来改变MakeSet
集合中元素的连通性,被合并的两个节点会变成一颗数,当然读者也可以实现自己的Union
,随意实现都行,只有调用Union
操作之后改变了MakeSet
,中图的连通性,是的u
,v
节点处于同一颗树就行,本文的Union
方法采用的思想是 按秩合并(秩 rank)、路径压缩
,通过这种方式创建的树的节点分布,会比较均匀,平衡性较高,也就导致操作效率很高
function Union(u, v) {let uRoot = Find(u);let vRoot = Find(v);// 如果 u 和 v 在同一颗树if (uRoot == vRoot) return;// 如果 u 和 v 不在同一颗树中,合并它们// 如果 uRoot 的层级比 vRoot 的小,将 uRoot 作为 vRoot 前驱节点if (uRoot.rank < vRoot.rank) uRoot.parent = vRoot;// 如果 uRoot 的层级比 vRoot 的大,将 vRoot 作为 uRoot 前驱节点else if (uRoot.rank > vRoot.rank) vRoot.parent = uRoot;//将 uRoot 设置为根节点,并将 uRoot 的层级加一else {vRoot.parent = uRoot;uRoot.rank = uRoot.rank + 1;}
}
- 4.5. Kruskal算法
Kruskal算法旨在寻找最小生成数中包含哪些边,在后面的完整代码中,该函数的实现会有所不同,这里着重体会原理
function Kruskal(G, w) {let A = []; //A用于存放最小生成数所包含的边for(let x of G.V) {MakeSet(x);}//对G.E按照边的权中从小到大排序for(let e of G.E) {quickSort(0, G.E.length-1, G.E, 'w');}//由于边已经按照从小到大的顺序有序,所以这里只需要寻找不相交的边(边所在的树不相交),for(let e of G.E) {if(Find(e.u)!=Find(e.v)) {A.push(e);Union(e.u, e.v); //改变连通性}}return A;
}
- 4.6. 图,顶点,边,的数据结构
这里的数据结构及如何建图参照 BFS,DFS 算法原理及js实现,这里不做详细说明
//顶点数据结构
function Vertex() {if (!(this instanceof Vertex))return new Vertex();this.edges = null; //由顶点发出的所有边this.id = null; //节点的唯一标识this.data = null; //存放节点的数据
}//数据结构 邻接链表-边
function Edge() {if (!(this instanceof Edge))return new Edge();this.index = null; //边所依附的节点的位置this.sibling = null;this.w = null; //保存边的权值
}//数据结构 图-G
function Graph() {if (!(this instanceof Graph))return new Graph();this.V = []; //节点集this.E = []; //边集this.refer = new Map(); //字典 用来映射标节点的识符和数组中的位置
}
Graph.prototype = {constructor: Graph,//这里加进来的已经具备了边的关系//创建图的 节点initVertex: function(vertexs) {//创建节点并初始化节点属性 idfor (let v of vertexs) {let vertex = Vertex();vertex.id = v.id;this.V.push(vertex);}//初始化 字典for (let i in this.V) {this.refer.set(this.V[i].id, i);}},//建立图中 边 的关系initEdge: (function() {//创建链表,返回链表的第一个节点function createLink(index, len, edges, refer) {if (index >= len) return null;let edgeNode = Edge();edgeNode.index = refer.get(edges[index].id); //边连接的节点 用在数组中的位置表示 参照字典edgeNode.w = edges[index].w; //边的权值edgeNode.sibling = createLink(++index, len, edges, refer); //通过递归实现 回溯return edgeNode;}return function(edges) {for (let field in edges) {let index = this.refer.get(field); //从字典表中找出节点在 V 中的位置let vertex = this.V[index]; //获取节点vertex.edges = createLink(0, edges[field].length, edges[field], this.refer);}}}()),storageEdge: function(edges) {this.E = edges;}
}var vertexs = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}];
var edges = [{u:'a',v:'b',w:3},{u:'a',v:'c',w:1},{u:'b',v:'a',w:3},{u:'b',v:'c',w:4},{u:'b',v:'d',w:5},{u:'c',v:'a',w:1},{u:'c',v:'b',w:4},{u:'c',v:'d',w:6},{u:'c',v:'e',w:7},{u:'d',v:'b',w:5},{u:'d',v:'c',w:6},{u:'d',v:'e',w:2},{u:'e',v:'c',w:7},{u:'e',v:'d',w:6}
]var g = Graph();
g.initVertex(vertexs);
g.storageEdge(edges);
运行这部分代码,生成了用于Kruskal算法输入的图
- 4.7. 完整代码及测试
测试的算法的输入图为上图,红色的边为最终最小生成树包含的边,出现顺序依次为 ac,de,ab,bd
,这里的输入图为无向图
//快速排序 数组a由对象组成 key为排序的参照指标 quickSort(0,a.length-1,a,'key')
function quickSort(left, right, a, key) {if (left > right)return;var i = left;var j = right;var benchMark = a[i];var temp;while (i != j) {//移动 jwhile (a[j][key] >= benchMark[key] && i < j)j--;//移动 iwhile (a[i][key] <= benchMark[key] && i < j)i++;if (i < j) {temp = a[i];a[i] = a[j];a[j] = temp;}}a[left] = a[i];a[i] = benchMark;quickSort(left, i - 1, a, key);quickSort(i + 1, right, a, key);
}var MakeSet = (function() {let set = new Set();return function(x) {x.parent = x;x.rank = 0;if (!set.has(x)) set.add(x);return set;}
})();//体会两个 Find 方法的不同
// function Find(x) {
// if (x.parent != x)
// Find(x.parent);
// return x.parent;
// }function Find(x) {if (x.parent != x)x.parent = Find(x.parent);return x.parent;
}function Union(u, v) {let uRoot = Find(u);let vRoot = Find(v);// 如果 u 和 v 在同一颗树if (uRoot == vRoot) return;// 如果 u 和 v 不在同一颗树中,合并它们// 如果 uRoot 的层级比 vRoot 的小,将 uRoot 作为 vRoot 前驱节点if (uRoot.rank < vRoot.rank) uRoot.parent = vRoot;// 如果 uRoot 的层级比 vRoot 的大,将 vRoot 作为 uRoot 前驱节点else if (uRoot.rank > vRoot.rank) vRoot.parent = uRoot;//任选一个作为根节点else {vRoot.parent = uRoot;uRoot.rank = uRoot.rank + 1;}
}function Kruskal(G) {let A = []; //A用于存放最小生成数所包含的边for(let x of G.V) {MakeSet(x);}//对G.E按照边的权中从小到大排序for(let e of G.E) {quickSort(0, G.E.length-1, G.E, 'w');}for(let e of G.E) {let u = G.V[G.refer.get(e.u)];let v = G.V[G.refer.get(e.v)];if(Find(u)!=Find(v)) {A.push(e);Union(u, v);}}return A;
}function Vertex() {if (!(this instanceof Vertex))return new Vertex();this.edges = null; //由顶点发出的所有边this.id = null; //节点的唯一标识this.data = null; //存放节点的数据
}//数据结构 邻接链表-边
function Edge() {if (!(this instanceof Edge))return new Edge();this.index = null; //边所依附的节点的位置this.sibling = null;this.w = null; //保存边的权值
}//数据结构 图-G
function Graph() {if (!(this instanceof Graph))return new Graph();this.V = []; //节点集this.E = [];this.refer = new Map(); //字典 用来映射标节点的识符和数组中的位置
}
Graph.prototype = {constructor: Graph,//这里加进来的已经具备了边的关系//创建图的 节点initVertex: function(vertexs) {//创建节点并初始化节点属性 idfor (let v of vertexs) {let vertex = Vertex();vertex.id = v.id;this.V.push(vertex);}//初始化 字典for (let i in this.V) {this.refer.set(this.V[i].id, i);}},//建立图中 边 的关系initEdge: (function() {//创建链表,返回链表的第一个节点function createLink(index, len, edges, refer) {if (index >= len) return null;let edgeNode = Edge();edgeNode.index = refer.get(edges[index].id); //边连接的节点 用在数组中的位置表示 参照字典edgeNode.w = edges[index].w; //边的权值edgeNode.sibling = createLink(++index, len, edges, refer); //通过递归实现 回溯return edgeNode;}return function(edges) {for (let field in edges) {let index = this.refer.get(field); //从字典表中找出节点在 V 中的位置let vertex = this.V[index]; //获取节点vertex.edges = createLink(0, edges[field].length, edges[field], this.refer);}}}()),storageEdge: function(edges) {this.E = edges;}
}//测试数据
var vertexs = [{id:'a'}, {id:'b'}, {id:'c'}, {id:'d'}, {id:'e'}];
var edges = [{u:'a',v:'b',w:3},{u:'a',v:'c',w:1},{u:'b',v:'a',w:3},{u:'b',v:'c',w:4},{u:'b',v:'d',w:5},{u:'c',v:'a',w:1},{u:'c',v:'b',w:4},{u:'c',v:'d',w:6},{u:'c',v:'e',w:7},{u:'d',v:'b',w:5},{u:'d',v:'c',w:6},{u:'d',v:'e',w:2},{u:'e',v:'c',w:7},{u:'e',v:'d',w:6}
]var g = Graph();
g.initVertex(vertexs);
g.storageEdge(edges);
var A = Kruskal(g);
console.log(A);
最小生成树原理及Kruskal算法的js实现相关推荐
- 最小生成树(Prim、Kruskal)算法,秒懂!
前言 在数据结构与算法的图论中,(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法.但是可能很多人对概念不是很清楚,什么是最小生成树? 一个有 n 个结点的连通图的生成树是原图的极小连通子 ...
- 最小生成树 kruskal_使用Kruskal算法求解Java最小生成树问题
最小生成树 kruskal In Electronic Circuit we often required less wiring to connect pins together. We can m ...
- 数据结构与算法A实验六图论---7-4 公路村村通(最小生成树Prime和Kruskal算法)
现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N) ...
- 克鲁斯卡尔算法c语言,最小生成树-克鲁斯卡尔(Kruskal)算法
1. 克鲁斯卡尔算法简介 克鲁斯卡尔算法是一种用来寻找最小生成树的算法(用来求加权连通图的最小生成树的算法).在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边. 而具体 ...
- 7、最小生成树,克鲁斯卡尔(Kruskal)算法
1)算法的基本思想: 前面我们学习过Prim算法,他是一种以某个节点出发,按权值递增的次序选择合适的边来构造最小生成树的方法,他的时间复杂度为O(n2),与顶点有关,而与边无边,所以适合求边稠密的图的 ...
- 数据结构之图的应用:最小生成树MST(prime算法和Kruskal算法)
图的应用:最小生成树 最小生成树的定义: 最小生成树的性质: Prime算法:(贪心算法思想) Prime算法的代码实现原理: Prime算法的实现代码: Prime算法的性能: Kruskal算法: ...
- C++ 实现无向图的最小生成树Kruskal算法(完整代码)
按照Kruskal思想,n个结点的生成树有n-1条边,故反复上述过程,直到选取了n-1条边为止,就构成了一棵最小生成树. 实现Kruskal算法的关键问题是: 当一条边加入T的边集中后,如何判断是否构 ...
- 作业1-采用Prim算法和Kruskal算法构造最小生成树
采用Prim算法和Kruskal算法构造最小生成树 实验报告 1.问题 2.解析 (1)Prim算法 (2)Kruskal算法 3.设计 (1)Prim算法 (2)Kruskal算法 4.分析 (1) ...
- 常用代码模板3——搜索与图论(Bellman-Ford算法 、spfa 算法、floyd算法、Kruskal算法、染色法、匈牙利算法 )
目录 一.树与图的存储 二.树与图的遍历 (1) 深度优先遍历 -- 模板题 AcWing 846. 树的重心 (2) 宽度优先遍历 -- 模板题 AcWing 847. 图中点的层次 拓扑排序 -- ...
最新文章
- 计算机应用基础上机操作,计算机应用基础上机操作试题
- 四、垃圾收集之垃圾收集算法
- PHP扩展开发入门3------带参数的函数
- 最基本的弹出窗口代码
- OSError: [WinError 126] 找不到指定的模块————Shapely
- 【转】关闭特定虚拟机上声音嘟嘟声
- SNF快速开发平台MVC-自由排序组件
- Android Ndef Message解析
- cas-server Jdbc 连接读取用户(5)
- 改变Eclipse标记高亮的颜色
- php中控车牌识别push协议,2、实时车牌识别上传及返回
- 接入华为webpush webpush总结
- GraphLite 实现子图匹配
- msxml6 C++
- Win10 将 Bookmarks 的书签恢复到 Chrome
- GPS从入门到放弃(一) --- GPS基础原理
- 数据表底层的B+树的叶子结点为啥用类似双链表连接起来
- 部署CentOS可视化界面GUI-之腾讯云服务器
- idea 的注释在格式化空格问题
- 上了 istio 的贼船之 API Gateway
热门文章
- ELK下Elasticsearch优化
- 【Shell】压缩相关命令
- 【转】SqlLite .Net 4.0 System.IO.FileLoadException”类型的未经处理的异常出现在XXX
- 【异常】Unable to instantiate SparkSession with Hive support because Hive classes a
- 【机器学习】如何解决数据不平衡问题
- 您如何性能测试JavaScript代码?
- location.host与location.hostname和跨浏览器的兼容性?
- 为什么Java有瞬态字段?
- 尝试安装pg gem时找不到#39;libpq-fe.h标头
- C ++ Singleton设计模式