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

Kruskal算法和Prim算法构造它的一棵最小代价生成树的过程

发布网友 发布时间:2022-05-15 00:14

我来回答

3个回答

懂视网 时间:2022-04-26 22:41

之前都是看书,大部分也是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-04-26 19:49

Prim算法复杂度:O(n2), 与边无关,适合求边稠密的网的最小生成树。

算法思想:假设N={V,{E}}是连通网,TE是N上最小生成树中边的集合。算法从U={u0},TE ={}开始,重复执行下述操作:在所有u∈U,v∈V-U的边(u,v)∈E中找一条代价最小的边(u0,v0)并入集合TE,同时v0并入U,直至U=V为止。

Kruskal算法复杂度:O(eloge),相对于Prim而言,适合求边稀疏的网的最小生成树。

算法思想:最小生成树的初始状态为只有n个顶点而无边的非连通图T=(V,{}),图中每个顶点自成一个连通分量。在E中选择代价最小的边,若该边依附的顶点落在T中不同的连通分量上,则将此边加入到T中,否则舍去次边而选择下一条代价最小的边。直至T中所有顶点都在同一连通分量上为止。

热心网友 时间:2022-04-26 21:07

算法同样是解决最小生成树的问题。

其算法为:在这n个点中的相通的边进行排序,然后不断地将边添加到集合中(体现了贪心的算法特点),在并入集合之前,必须检查一下这两点是不是在一个集合当中,这就用到了并查集的知识。直到边的集合达到了n-1个。

与prim算法的不同:prim算法为单源不断寻找连接的最短边,向外扩展,即单树形成森林。而Kruskal算法则是不断寻找最短边然后不断将集合合并,即多树形成森林。

复杂度的不同:prim算法的复杂度是O(n^2),其中n为点的个数。Kruskal算法的复杂度是O(e*loge),其中e为边的个数。两者各有优劣,在不同的情况下选择不同的算法。

Prim算法用于求无向图的最小生成树

设图G =(V,E),其生成树的顶点集合为U。

①、把v0放入U。

②、在所有u∈U,v∈V-U的边(u,v)∈E中找一条最小权值的边,加入生成树。

③、把②找到的边的v加入U集合。如果U集合已有n个元素,则结束,否则继续执行②。

其算法的时间复杂度为O(n^2)

Prim算法实现:

(1)集合:设置一个数组set(i=0,1,..,n-1),初始值为 0,代表对应顶点不在集合中(注意:顶点号与下标号差1)

(2)图用邻接阵表示,路径不通用无穷大表示,在计算机中可用一个大整数代替。
{先选定一个点,然后从该点出发,与该点相连的点取权值最小者归入集合,然后再比较在集合中的两点与其它各点的边的权值最小者,再次进入集合,一直到将所有的点都归入集合为止。}
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
打印机硒鼓安装方法及步骤 如何安装新硒鼓? 我现在很困惑该不该和女友继续发展下去 电脑上字体怎么安装方法简单易行的字体安装步骤及技巧 网页字体显示不出.怎么办? 如何添加电脑上没有的字体解决电脑字体限制增加字体选择多样性_百度知 ... 不有效字体文件 电脑字体识别不了 梦见身上煤炭黑的长辈给我钥匙的预兆 株洲最值得一去的古镇 讲一讲你的童年经历过哪些有趣的事? 夏天吃啥水果能防暑? 过年期间你经历过最有趣的事情是什么? 2021款本田思域三厢版海外上市 推出2.0L和1.5T两种动力总成 你在大自然的怀抱里经历过哪些有趣的事,先说说,再写下来? 在你的成长过程中经历了哪些有趣又难忘的事? 夏天可以通过食用哪些水果来防暑? 我这个空调耗电量怎么计算? 用英语写一篇有你的经历中最有趣的事(追加100分) 广州那里有尾货帆布包包 你曾经经历过哪些有趣的事情? 你经历过的最有趣的事情是什么呢? 汽车之家本田思域发动机皮带多少公里更换 你都在上学途中经历过哪些有趣的事情? 广州包包尾货市场有个什么井的 海淀南路6号楼交通方便吗?应该怎么过去? 北京海淀区有几家吉野家?都在哪里? 北京海淀南路5号楼邮编 谁能帮我查查北京海淀南路8号楼2层有几家住户谢谢! 人民大学附近有没有便宜的旅馆? 考研期间经历的最有趣的事情是什么? 你在超市经历过哪些有趣的事情? 红尘来去一场梦谁唱的好听? 本田crv经典升级版,怎么汽车之家找不到这个型号版本的。这版本是不是 说一说学生时代你经历过的最有趣的事是什么? 吃那种水果防暑啊? 红尘来去一场梦 蒙面歌王谁唱的 这种天气吃什么水果最解暑呢? 有向图的最小树形图为什么不能用prim? 你曾在国外经历过什么有趣的事情? 红尘来去一场梦灭火英雄里的是谁唱的 红尘来去一场梦歌词:是浮各一朝还是浮名一朝? 最新一期蒙面歌王杨宗纬唱得什么歌 红尘来去一场梦是不是那个电影的歌曲 什么水果能防暑? 红尘来去一场梦 普罗米修斯是谁 红尘来去一场梦的含义是什么? 蒙面歌王红尘来去一场梦是谁唱的 蒙面唱将猜猜猜红尘来去一场梦是哪一季 杨宗纬唱的红尘来去一场梦是我是歌手那一期的