JS使用Prim算法和Kruskal算法实现最小生成树


Posted in Javascript onJanuary 17, 2019

之前都是看书,大部分也是c++的实现,但是搞前端不能忘了JS啊,所以JS实现一遍这两个经典的最小生成树算法。

一、权重图和最小生成树

权重图:图的边带权重

最小生成树:在连通图的所有生成树中,所有边的权重和最小的生成树

本文使用的图如下:

JS使用Prim算法和Kruskal算法实现最小生成树

它的最小生成树如下:

JS使用Prim算法和Kruskal算法实现最小生成树

二、邻接矩阵

邻接矩阵:用来表示图的矩阵就是邻接矩阵,其中下标表示顶点,矩阵中的值表示边的权重(或者有无边,方向等)。

本文在构建邻接矩阵时,默认Number.MAX_SAFE_INTEGER表示两个节点之间没有边,Number.MIN_SAFE_INTEGER表示当前节点没有自环。

代码如下:

/**
 * 邻接矩阵
 * 值为顶点与顶点之间边的权值,0表示无自环,一个大数表示无边(比如10000)
 * */
const MAX_INTEGER = Number.MAX_SAFE_INTEGER;//没有的边
const MIN_INTEGER = Number.MIN_SAFE_INTEGER;//没有自环
 
const matrix= [
  [MIN_INTEGER, 9, 2, MAX_INTEGER, 6],
  [9, MIN_INTEGER, 3, MAX_INTEGER, MAX_INTEGER],
  [2, 3, MIN_INTEGER, 5, MAX_INTEGER],
  [MAX_INTEGER, MAX_INTEGER, 5, MIN_INTEGER, 1],
  [6, MAX_INTEGER, MAX_INTEGER, 1, MIN_INTEGER]
];

这个邻接矩阵表示的图如下:

三、 边的表示

一个边具有权重、起点、重点三个属性,所以可以创建一个类(对象),实现如下:

/**
 * 边对象
 * */
function Edge(begin, end, weight) {
  this.begin = begin;
  this.end = end;
  this.weight = weight;
}
 
Edge.prototype.getBegin = function () {
  return this.begin;
};
Edge.prototype.getEnd = function () {
  return this.end;
};
Edge.prototype.getWeight = function () {
  return this.weight;
};
 
/*class Edge {
  constructor(begin, end, weight) {
    this.begin = begin;
    this.end = end;
    this.weight = weight;
  }
  getBegin() {
    return this.begin;
  }
  getEnd() {
    return this.end;
  }
  getWeight() {
    return this.weight;
  }
}*/

 PS:JS这门语言没有私有变量的说法,这里写get方法纯粹是模拟一下私有变量。可以不用这么写,可以直接通过属性访问到属性值。

四、Prim算法

将这个算法的文章数不胜数,这里就不细说了。

其大体思路就是:以某顶点为起点,逐步找各顶点上最小权值的相邻边构建最小生成树,同时其邻接点纳入生成树的顶点中,只要保证顶点不重复添加即可。

实现代码如下:

/**
 * Prim算法
 * 以某顶点为起点,逐步找各顶点上最小权值的边构建最小生成树,同时其邻接点纳入生成树的顶点中,只要保证顶点不重复添加即可
 * 使用邻接矩阵即可
 * 优点:适合点少边多的情况
 * @param matrix 邻接矩阵
 * @return Array 最小生成树的边集数组
 * */
function prim(matrix) {
  const rows = matrix.length,
    cols = rows,
    result = [],
    savedNode = [0];//已选择的节点
  let minVex = -1,
    minWeight = MAX_INTEGER;
  for (let i = 0; i < rows; i++) {
    let row = savedNode[i],
      edgeArr = matrix[row];
    for (let j = 0; j < cols; j++) {
      if (edgeArr[j] < minWeight && edgeArr[j] !== MIN_INTEGER) {
        minWeight = edgeArr[j];
        minVex = j;
      }
    }
 
    //保证所有已保存节点的相邻边都遍历到
    if (savedNode.indexOf(minVex) === -1 && i === savedNode.length - 1) {
      savedNode.push(minVex);
      result.push(new Edge(row, minVex, minWeight));
 
      //重新在已加入的节点集中找权值最小的边的外部边
      i = -1;
      minWeight = MAX_INTEGER;
 
      //已加入的边,去掉,下次就不会选这条边了
      matrix[row][minVex] = MAX_INTEGER;
      matrix[minVex][row] = MAX_INTEGER;
    }
  }
  return result;
}

五、Kruskal算法

介绍这个算法的文章也很多,这里不细说。

其主要的思路就是:遍历所有的边,按权值从小到大排序,每次选取当前权值最小的边,只要不构成回环,则加入生成树。

5.1 邻接矩阵转成边集数组

与Prim算法不同,Kruskal算法是从最小权值的边开始的,所以使用边集数组更方便。所以需要将邻接矩阵转成边集数组,并且按照边的权重从小到大排序。

/**
 * 邻接矩阵转边集数组的函数
 * @param matrix 邻接矩阵
 * @return Array 边集数组
 * */
function changeMatrixToEdgeArray(matrix) {
  const rows = matrix.length,
    cols = rows,
    result = [];
  for (let i = 0; i < rows; i++) {
    const row = matrix[i];
    for(let j = 0 ; j < cols; j++) {
      if(row[j] !== MIN_INTEGER && row[j] !== MAX_INTEGER) {
        result.push(new Edge(i, j, row[j]));
        matrix[i][j] = MAX_INTEGER;
        matrix[j][i] = MAX_INTEGER;
      }
    }
  }
  result.sort((a, b) => a.getWeight() - b.getWeight());
  return result;
}

5.2 Kruskal算法的具体实现

Kruskal算法的一个要点就是避免环路,这里采用一个数组来保存已纳入生成树的顶点和边(连线),其下标是边(连线)的起点,下标对应的元素值是边(连线)的终点。下标对应的元素值为0,表示还没有以它为起点的边(连线)。

连线:表示一条或多条边前后连接形成的一条线,这条线没有环路。

/**
 * kruskal算法
 * 遍历所有的边,按权值从小到大排序,每次选取当前权值最小的边,只要不构成回环,则加入生成树
 * 邻接矩阵转换成边集数组
 * 优点:适合点多边少的情况
 * @param matrix 邻接矩阵
 * @return Array 最小生成树的边集数组
 * */
function kruskal(matrix) {
  const edgeArray = changeMatrixToEdgeArray(matrix),
    result = [],
    //使用一个数组保存当前顶点的边的终点,0表示还没有已它为起点的边加入
    savedEdge = new Array(matrix.length).fill(0);
 
  for (let i = 0, len = edgeArray.length; i < len; i++) {
    const edge = edgeArray[i];
    const n = findEnd(savedEdge, edge.getBegin());
    const m = findEnd(savedEdge, edge.getEnd());
    console.log(savedEdge, n, m);
    //不相等表示这条边没有与现有生成树形成环路
    if (n !== m) {
      result.push(edge);
      //将这条边的结尾顶点加入数组中,表示顶点已在生成树中
      savedEdge[n] = m;
    }
  }
  return result;
}
 
/**
 * 查找连线顶点的尾部下标
 * @param arr 判断边与边是否形成环路的数组
 * @param start 连线开始的顶点
 * @return Number 连线顶点的尾部下标
 * */
function findEnd(arr, start) {
  //就是一直循环,直到找到终点,如果没有连线,就返回0
  while (arr[start] > 0) {
    start = arr[start];
  }
  return start;
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
node.js中的fs.utimesSync方法使用说明
Dec 15 Javascript
jQuery实现MSN中文网滑动Tab菜单效果代码
Sep 09 Javascript
js计算文本框输入的字符数
Oct 23 Javascript
AngularJS基础 ng-paste 指令简单示例
Aug 02 Javascript
jQuery+正则+文本框只能输入数字的实现方法
Oct 07 Javascript
JS添加或修改控件的样式(Class)实现方法
Oct 15 Javascript
用jQuery实现可输入多选下拉组合框实例代码
Jan 18 Javascript
JavaScript中splice与slice的区别
May 09 Javascript
前端构建工具之gulp的语法教程
Jun 12 Javascript
vue实现图片懒加载的方法分析
Feb 05 Javascript
微信小程序保持session会话的方法
Mar 20 Javascript
浅谈Web Storage API的使用
Jun 23 Javascript
微信小程序使用wxParse解析html的方法示例
Jan 17 #Javascript
nvm、nrm、npm 安装和使用详解(小结)
Jan 17 #Javascript
JavaScript之实现一个简单的Vue示例
Jan 17 #Javascript
如何能分清npm cnpm npx nvm
Jan 17 #Javascript
JavaScript设计模式之装饰者模式实例详解
Jan 17 #Javascript
npm 常用命令详解(小结)
Jan 17 #Javascript
JavaScript设计模式之享元模式实例详解
Jan 17 #Javascript
You might like
模拟OICQ的实现思路和核心程序(二)
2006/10/09 PHP
php的list()的一步操作给一组变量进行赋值的使用
2011/05/18 PHP
关于Javascript模块化和命名空间管理的问题说明
2010/12/06 Javascript
可恶的ie8提示缺少id未定义
2014/03/20 Javascript
js获取当前页面的url网址信息
2014/06/12 Javascript
jQuery焦点控制图层展示延迟隐藏的方法
2015/03/09 Javascript
jquery实现Li滚动时滚动条自动添加样式的方法
2015/08/10 Javascript
AngularJS基础 ng-options 指令详解
2016/08/02 Javascript
详细解读Jquery各Ajax函数($.get(),$.post(),$.ajax(),$.getJSON())
2016/08/15 Javascript
手机软键盘弹出时影响布局的解决方法
2016/12/15 Javascript
Javascript中的prototype与继承
2017/02/06 Javascript
微信小程序 sha1 实现密码加密实例详解
2017/07/06 Javascript
vue-router2.0 组件之间传参及获取动态参数的方法
2017/11/10 Javascript
深入理解node.js http模块
2018/01/24 Javascript
解决vue中修改export default中脚本报一大堆错的问题
2018/08/27 Javascript
typescript nodejs 依赖注入实现方法代码详解
2019/07/21 NodeJs
[01:10:30]DOTA2-DPC中国联赛正赛 Dragon vs Dynasty BO3 第一场 3月4日
2021/03/11 DOTA
python七夕浪漫表白源码
2019/04/05 Python
python中的decimal类型转换实例详解
2019/06/26 Python
Python3环境安装Scrapy爬虫框架过程及常见错误
2019/07/12 Python
python excel转换csv代码实例
2019/08/26 Python
python程序中的线程操作 concurrent模块使用详解
2019/09/23 Python
python调用函数、类和文件操作简单实例总结
2019/11/29 Python
FILA斐乐中国官方商城:意大利运动品牌
2017/01/25 全球购物
马来西亚网上美容店:Hermo.my
2017/11/25 全球购物
德国高端单身人士交友网站:ElitePartner
2018/12/02 全球购物
C语言面试题
2013/05/19 面试题
校园摄影活动策划方案
2014/02/05 职场文书
质监局领导班子对照检查材料思想汇报
2014/09/27 职场文书
党员对十八届四中全会的期盼思想汇报范文
2014/10/17 职场文书
新员工入职欢迎词
2015/01/23 职场文书
工作岗位职责范本
2015/02/15 职场文书
2015年扶贫帮困工作总结
2015/05/20 职场文书
标枪加油稿
2015/07/22 职场文书
小型企业的绩效考核制度模板
2019/11/21 职场文书
python套接字socket通信
2022/04/01 Python