js实现无向图Dijkstra算法

8个点,10条边的无向图

先抽象出路线source,顺便一提有向图和无向图最大的区别在于,0-4和4-0无向图表示的是同一个连通权重,有向图则不然(不仅权重不同,甚至可能只有一个方向连通)

const source=[{key:[0,1],value:3},{key:[0,7],value:1},{key:[0,4],value:5},{key:[0,3],value:2},{key:[1,6],value:7},{key:[2,6],value:2},{key:[2,3],value:4},{key:[3,4],value:8},{key:[4,5],value:1},{key:[5,6],value:4}]; //各条边
const points=[0,1,2,3,4,5,6,7]; //各点
//其他点一样的,此处为了看起来舒服设置0为起点6为终点,7是孤立点
const start =0;
const end =6;

此处的key,如果成为两个点的经纬度坐标,value成为距离或者时间形成的权重,则成为了GIS地图路网简单的最优解算法

let paths = points.filter(v=>{return v!=0}).map(v=>{let obj=source.find(vv=>{return vv.key.join("")==('0'+v)||vv.key.join("")==(v+"0");}); return {key:[0,v],value:obj && obj.value}}); //此处获取初始的从0到各个点的连通值(没有的直接连通的就是undefined)
let alreadyCalcPoint=[0]; //存储已经作为松弛顶点的点(算法核心就在于为每个点都去做一遍相邻点最小权重,并更新paths方案)

//得到上一步中value最小值的key(也可以在上一步里做,此处分开做,多遍历了一次)
//写成函数,后续反复会用到
function getMinSide(paths){
	let min=null;
	paths.map(v=>{
	    let otherPoint = v.key[0]==start?v.key[v.key.length-1]:v.key[0]; //去除start的
	    if(alreadyCalcPoint.indexOf(otherPoint)<0 && v.value){
			if(min){
				min=min.value<v.value?min:v
			}	
			else{
				min=v;
			}
		}
	});
	return min;
}

function findNextPoint(){
//找下一个松弛点
	//先一步步计算,后面再抽象出通用方法
	let minItem = getMinSide(paths); //得出的是 0-7 权重最小只有1
	if(minItem==null){
		return null; //算法结束的时候就是找不到还没有经过松弛操作的顶点
	}
	let nextPoint=minItem.key[0]==start?minItem.key[minItem.key.length-1]:minItem.key[0]; //例如后续minItem.key=[1,2,3],即路径中要经过3,则实际上该路径还是[1,3]
	alreadyCalcPoint.push(nextPoint);
	return minItem; //返回的是path对象
}

//计算得出以对应point为后顶点的权重
function calcPath(point){
	paths.map(v=>{
		let key=v.key[0]+''+v.key[v.key.length-1]; //实际的路径起止
		if(key==(start+''+point)||key==(point+''+start)){
			source.filter(vv=>{
				return (vv.key[0]==point||vv.key[1]==point)&&vv.key.join("")!=(start+''+point)&&  vv.key.join("")!=(start+''+point);
			}).map(vvv=>{
			//例如0-3-2的情况 point是3 anotherPoint则是2,tgObj表示的是0-?-2的路线
				let anotherPoint = vvv.key[0]==point?vvv.key[1]:vvv.key[0];
				let tgObj=paths.find(vvvv=>{
					return vvvv.key[vvvv.key.length-1]==anotherPoint;
				})
				if(tgObj){
					if(!(tgObj.value && tgObj.value<(v.value+vvv.value))){
						tgObj.value=((v.value||0)+vvv.value);
						tgObj.key = nextItem.key.concat(tgObj.key[tgObj.key.length-1]) //即当前松弛顶点的路径替换原有的路径
					}
				}
			});
		}
	});
}

最后就是用while循环一下,最优解的算法最大的特点(也是最容易产生性能消耗的一点)就在于,只有全部点都经历过一次松弛(即试一遍是否最优)才能得出准确的最优路径.优化空间几乎无,但是,如果只是求次优解,那么可操作模式很多,例如分块设置关键点(即最近路线很可能走的一些点,在导航中则为重点交通枢纽或者主要道路交叉口)

let nextItem = {};
while(nextItem !=null){
   nextItem = findNextPoint();
   if(nextItem!=null){
		let nextPoint = alreadyCalcPoint[alreadyCalcPoint.length-1];
   		calcPath(nextPoint);
   		console.log(nextPoint,JSON.stringify(paths)); //输出一下每次的顶点和0起始的最优解情况
   }
}

执行结果,最后的6那行就是最后结果,只需要取[0,3,2,6]的路径就是0-6的最优解

 7 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,2]},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,5]},{\"key\":[0,6]},{\"key\":[0,7],\"value\":1}]"
 3 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,5]},{\"key\":[0,6]},{\"key\":[0,7],\"value\":1}]"
 1 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,5]},{\"key\":[0,1,6],\"value\":10},{\"key\":[0,7],\"value\":1}]"
 4 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,4,5],\"value\":6},{\"key\":[0,1,6],\"value\":10},{\"key\":[0,7],\"value\":1}]"
 5 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,4,5],\"value\":6},{\"key\":[0,4,5,6],\"value\":10},{\"key\":[0,7],\"value\":1}]"
 2 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,4,5],\"value\":6},{\"key\":[0,3,2,6],\"value\":8},{\"key\":[0,7],\"value\":1}]"
 6 "[{\"key\":[0,1],\"value\":3},{\"key\":[0,3,2],\"value\":6},{\"key\":[0,3],\"value\":2},{\"key\":[0,4],\"value\":5},{\"key\":[0,4,5],\"value\":6},{\"key\":[0,3,2,6],\"value\":8},{\"key\":[0,7],\"value\":1}]"

Dijkstra算法每一次找松弛点的过程都是贪心算法的完美实践,每次都寻找到最优路径起始点到对应点的最优路径,直到全部点都遍历一遍,得出起始点到其他点的最有路线

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值