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 算法

KruskalPrim 算法是最小生成树常用的两种算法,这两种算法都是对上述通用方法的细化,不同之处就是对边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,中图的连通性,是的uv节点处于同一颗树就行,本文的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实现相关推荐

  1. 最小生成树(Prim、Kruskal)算法,秒懂!

    前言 在数据结构与算法的图论中,(生成)最小生成树算法是一种常用并且和生活贴切比较近的一种算法.但是可能很多人对概念不是很清楚,什么是最小生成树? 一个有 n 个结点的连通图的生成树是原图的极小连通子 ...

  2. 最小生成树 kruskal_使用Kruskal算法求解Java最小生成树问题

    最小生成树 kruskal In Electronic Circuit we often required less wiring to connect pins together. We can m ...

  3. 数据结构与算法A实验六图论---7-4 公路村村通(最小生成树Prime和Kruskal算法)

    现有村落间道路的统计数据表中,列出了有可能建设成标准公路的若干条道路的成本,求使每个村落都有公路连通所需要的最低成本. 输入格式: 输入数据包括城镇数目正整数N(≤1000)和候选道路数目M(≤3N) ...

  4. 克鲁斯卡尔算法c语言,最小生成树-克鲁斯卡尔(Kruskal)算法

    1. 克鲁斯卡尔算法简介 克鲁斯卡尔算法是一种用来寻找最小生成树的算法(用来求加权连通图的最小生成树的算法).在剩下的所有未选取的边中,找最小边,如果和已选取的边构成回路,则放弃,选取次小边. 而具体 ...

  5. 7、最小生成树,克鲁斯卡尔(Kruskal)算法

    1)算法的基本思想: 前面我们学习过Prim算法,他是一种以某个节点出发,按权值递增的次序选择合适的边来构造最小生成树的方法,他的时间复杂度为O(n2),与顶点有关,而与边无边,所以适合求边稠密的图的 ...

  6. 数据结构之图的应用:最小生成树MST(prime算法和Kruskal算法)

    图的应用:最小生成树 最小生成树的定义: 最小生成树的性质: Prime算法:(贪心算法思想) Prime算法的代码实现原理: Prime算法的实现代码: Prime算法的性能: Kruskal算法: ...

  7. C++ 实现无向图的最小生成树Kruskal算法(完整代码)

    按照Kruskal思想,n个结点的生成树有n-1条边,故反复上述过程,直到选取了n-1条边为止,就构成了一棵最小生成树. 实现Kruskal算法的关键问题是: 当一条边加入T的边集中后,如何判断是否构 ...

  8. 作业1-采用Prim算法和Kruskal算法构造最小生成树

    采用Prim算法和Kruskal算法构造最小生成树 实验报告 1.问题 2.解析 (1)Prim算法 (2)Kruskal算法 3.设计 (1)Prim算法 (2)Kruskal算法 4.分析 (1) ...

  9. 常用代码模板3——搜索与图论(Bellman-Ford算法 、spfa 算法、floyd算法、Kruskal算法、染色法、匈牙利算法 )

    目录 一.树与图的存储 二.树与图的遍历 (1) 深度优先遍历 -- 模板题 AcWing 846. 树的重心 (2) 宽度优先遍历 -- 模板题 AcWing 847. 图中点的层次 拓扑排序 -- ...

最新文章

  1. 计算机应用基础上机操作,计算机应用基础上机操作试题
  2. 四、垃圾收集之垃圾收集算法
  3. PHP扩展开发入门3------带参数的函数
  4. 最基本的弹出窗口代码
  5. OSError: [WinError 126] 找不到指定的模块————Shapely
  6. 【转】关闭特定虚拟机上声音嘟嘟声
  7. SNF快速开发平台MVC-自由排序组件
  8. Android Ndef Message解析
  9. cas-server Jdbc 连接读取用户(5)
  10. 改变Eclipse标记高亮的颜色
  11. php中控车牌识别push协议,2、实时车牌识别上传及返回
  12. 接入华为webpush webpush总结
  13. GraphLite 实现子图匹配
  14. msxml6 C++
  15. Win10 将 Bookmarks 的书签恢复到 Chrome
  16. GPS从入门到放弃(一) --- GPS基础原理
  17. 数据表底层的B+树的叶子结点为啥用类似双链表连接起来
  18. 部署CentOS可视化界面GUI-之腾讯云服务器
  19. idea 的注释在格式化空格问题
  20. 上了 istio 的贼船之 API Gateway

热门文章

  1. ELK下Elasticsearch优化
  2. 【Shell】压缩相关命令
  3. 【转】SqlLite .Net 4.0 System.IO.FileLoadException”类型的未经处理的异常出现在XXX
  4. 【异常】Unable to instantiate SparkSession with Hive support because Hive classes a
  5. 【机器学习】如何解决数据不平衡问题
  6. 您如何性能测试JavaScript代码?
  7. location.host与location.hostname和跨浏览器的兼容性?
  8. 为什么Java有瞬态字段?
  9. 尝试安装pg gem时找不到#39;libpq-fe.h标头
  10. C ++ Singleton设计模式