JS实现最小生成树之克鲁斯卡尔(Kruskal)算法
克鲁斯卡尔算法打印最小生成树:
构造出所有边的集合 edges,从小到大,依次选出筛选边打印,遇到闭环(形成回路)时跳过。
JS代码:
1 //定义邻接矩阵 2 let Arr2 = [ 3 [0, 10, 65535, 65535, 65535, 11, 65535, 65535, 65535], 4 [10, 0, 18, 65535, 65535, 65535, 16, 65535, 12], 5 [65535, 18, 0, 22, 65535, 65535, 65535, 65535, 8], 6 [65535, 65535, 22, 0, 20, 65535, 65535, 16, 21], 7 [65535, 65535, 65535, 20, 0, 26, 65535, 7, 65535], 8 [11, 65535, 65535, 65535, 26, 0, 17, 65535, 65535], 9 [65535, 16, 65535, 65535, 65535, 17, 0, 19, 65535], 10 [65535, 65535, 65535, 16, 7, 65535, 19, 0, 65535], 11 [65535, 12, 8, 21, 65535, 65535, 65535, 65535, 0], 12 ] 13 14 let numVertexes = 9, //定义顶点数 15 numEdges = 15; //定义边数 16 17 // 定义图结构 18 function MGraph() { 19 this.vexs = []; //顶点表 20 this.arc = []; // 邻接矩阵,可看作边表 21 this.numVertexes = null; //图中当前的顶点数 22 this.numEdges = null; //图中当前的边数 23 } 24 let G = new MGraph(); //创建图使用 25 26 //创建图 27 function createMGraph() { 28 G.numVertexes = numVertexes; //设置顶点数 29 G.numEdges = numEdges; //设置边数 30 31 //录入顶点信息 32 for (let i = 0; i < G.numVertexes; i++) { 33 G.vexs[i] = \'V\' + i; //scanf(\'%s\'); //ascii码转字符 //String.fromCharCode(i + 65); 34 } 35 console.log(G.vexs) //打印顶点 36 37 //邻接矩阵初始化 38 for (let i = 0; i < G.numVertexes; i++) { 39 G.arc[i] = []; 40 for (j = 0; j < G.numVertexes; j++) { 41 G.arc[i][j] = Arr2[i][j]; //INFINITY; 42 } 43 } 44 console.log(G.arc); //打印邻接矩阵 45 } 46 47 function Edge() { 48 this.begin = 0; 49 this.end = 0; 50 this.weight = 0; 51 } 52 53 function Kruskal() { 54 let n, m; 55 let parent = []; //定义一数组用来判断边与边是否形成环路 56 let edges = []; //定义边集数组 57 58 for (let i = 0; i < G.numVertexes; i++) { 59 for (let j = i; j < G.numVertexes; j++) { //因为是无向图所以相同的边录入一次即可,若是有向图改为0 60 if (G.arc[i][j] != 0 && G.arc[i][j] != 65535) { 61 let edge = new Edge(); 62 edge.begin = i; 63 edge.end = j; 64 edge.weight = G.arc[i][j]; 65 edges.push(edge); 66 } 67 } 68 } 69 70 edges.sort((v1, v2) => { 71 return v1.weight - v2.weight 72 }); 73 74 console.log(\'**********打印所有边*********\'); 75 console.log(edges); 76 77 for (let i = 0; i < G.numVertexes; i++) { 78 parent[i] = 0; 79 } 80 81 for (let i = 0; i < edges.length; i++) { 82 n = Find(parent, edges[i].begin) 83 m = Find(parent, edges[i].end) 84 if (n != m) { //假如n与m不等,说明此边没有与现有生成树形成环路 85 parent[n] = m; 86 console.log("(%s,%s) %d", G.vexs[edges[i].begin], G.vexs[edges[i].end], edges[i].weight); 87 } 88 } 89 } 90 91 92 function Find(parent, f) { //查找连线顶点的尾部下标 93 while (parent[f] > 0) { 94 f = parent[f] 95 } 96 return f; 97 } 98 99 createMGraph(); 100 console.log(\'*********打印最小生成树**********\') 101 Kruskal();
打印结果:
代码部分过程解析:
当i=7时,第82行,调用Find函数,会传入参数edges[7].begin=5。此时第94行,parent[5]=8>0,所以f=8,再循环得parent[8]=6。因parent[6]=0 所以Find返回后第82行得到n=6。而此时第83行,传入参数edges[7].end=6得到m=6。此时n=m,不再打印,继续下一循环。这就告诉我们,因为(V5,V6)使得边集合A形成了环路。因此不能将它纳入到最小生成树中。
当i=8时,与上面相同,由于边(V1,V2)使得边集合A形成了环路,因此不将它纳入最小生成树。
克鲁斯卡尔算法主要针对边展开,时间复杂度为 O(elog e),e为图的边数,普利姆算法的时间复杂度为O(n²),n为最小生成树的边数。所以,边数少(稀疏图)用克鲁斯卡尔算法,边数多(稠密图)用普利姆算法。
参考文献: 程杰《大话数据结构》
版权声明:本文为xbblogs原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。