克鲁斯卡尔算法打印最小生成树:
构造出所有边的集合 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 functionMGraph() {19 this.vexs = []; //顶点表
20 this.arc = []; //邻接矩阵,可看作边表
21 this.numVertexes = null; //图中当前的顶点数
22 this.numEdges = null; //图中当前的边数
23 }24 let G = new MGraph(); //创建图使用
25
26 //创建图
27 functioncreateMGraph() {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 functionEdge() {48 this.begin = 0;49 this.end = 0;50 this.weight = 0;51 }52
53 functionKruskal() {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 = newEdge();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.weight72 });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 returnf;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为最小生成树的边数。所以,边数少(稀疏图)用克鲁斯卡尔算法,边数多(稠密图)用普利姆算法。
参考文献: 程杰《大话数据结构》