问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

13.用Prim算法和Kruskal算法构造图的最小生成树,所得到的最小生成树是 ...

发布网友 发布时间:2022-05-15 09:52

我来回答

2个回答

懂视网 时间:2022-05-18 23:39

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

一、权重图和最小生成树

权重图:图的边带权重

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

本文使用的图如下:

它的最小生成树如下:

二、邻接矩阵

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

本文在构建邻接矩阵时,默认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;
}

热心网友 时间:2022-05-18 20:47

如果原来的图里面任何两条边长都不相同,那么最小生成树是唯一的,此时不管用什么方法算出来的都是一样的
但是如果图里有相等的边,那么最小生成树可能会不唯一,这样就无法保证不同的方法得到同一棵树(即使是同一个算法,只要图的编号方式改变也可能得到不同的最小生成树)
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
为什么来大姨妈胸会胀 少儿学什么舞蹈 青年学什么舞蹈好 成年人学什么舞蹈 福州企业最低工资标准 2013年厦门的底薪是多少 生产要素的需求有哪些性质 生产要素的需求有何特点? 什么是生产要素需求 微观经济学要素需求什么是条件要素需求?它和要素需求有什么不同?_百度... 根据PRIM算法构造最小生成树怎么确定出发点? 微信步数怎么还会越变越少? 用普里姆算法构造如图所示的图G的一棵最小生成树。 征信不太好,买车的话审批好通过吗? 用普里姆(Prim)算法求出下图的最小生成树。 华中科技大学的研究生毕业论文在哪里可以查到 南京大学研究生毕业论文查重率多少 清华规定申请硕士学位不必发表论文,申请学士学位要发布论文吗? 梦到骑电瓶车车会发生什么 广东工业大学的硕士学位论文上传在哪个网站 青岛科技大学研究生发几篇论文 梦见我和死亡的村民一起去买电瓶车是好是坏 如何检索某一大学在2010-2014年发表的博士学位论文或优秀硕士学位论文 介绍一下清朝词人纳兰容若??? 清朝有名的作词诗人有谁?以及写出作品内容! 清朝诗人陈什么崧? 介绍一下清朝的词人纳兰性德 清朝三大词人除了纳兰性德另外两个是谁? 清代三大词人代表作 清代词人纳兰性德被誉为什么 QQ健康,微信健康捐了步数会不会影响排名 4.用Prim算法求下图的最小生成树, 若从顶点0出发,请将算法中的两个辅助数组的变化过程填入下表。 用普里姆(Prim)或克鲁斯卡尔(Kruskal)算法,画出下列无向网的最小生成树 为用Prim算法求最小生成树,需要哪些辅助变量 用普里姆算法求最小生成树(C++) c++求最小生成树prim算法,我捣鼓2天了,真心不会改了,求指导感激不尽啊 在用prim算法求解最小生成树的程序的基础上如何做修改使得能输出所有的最小生成树 锦鲤鱼要怎样换水吖? 怎样给锦鲤换水 锦鲤怎么换水? 给锦鲤鱼换水我用一层布把原来的脏水在哪里过滤一下就可以了吗? 锦鲤怎么换水 养锦鲤正确的换水时间是什么时候 锦鲤换水的时候可以直接换自来水不 锦鲤鱼换水的方法如下 我每天都喝银耳莲子百合薏米芡实红豆糯米粥,不知道这几样东西一起放合适吗? 莲子 百合 红豆 薏米有什么好处 焦虑性神经官能症是啥? 神经官能症的焦虑应该怎么治疗? 焦虑性神经官能症的病因