js版本无环加权有向图的最短路径算法

function deleteMin(pq){
    pq.sort(lengthSort)
    return pq.shift()
}
//function lengthSort(a,b){return a[2]<b[2]?-1:1}
function lengthSort(a,b){return a[2]-b[2]}


/*因为dijkstra算法不能出现有向环,这个方法判断是否有环,有就删除遍历时找到的最后一条边
最终的检测方法只能检测有环非负图,因为如果dijkstra里有负边会导致start不到的点也被relax,
虽然对逻辑没影响但会修改from和distTo的值导致test测试的结果不准确。
*/
function testHasCycleAndDeleteOne(){
    var marked=[]
    var from=[]
    var onStack=[]
    for(let i=0;i<V;i++){
        testHasCycleAndDeleteOne_(i,marked,onStack,from)
        if(hasCycle){
            return
        }
    }
    
}
function testHasCycleAndDeleteOne_(v,marked,onStack,from){
    marked[v]=true
    onStack[v]=true
    for(let j in G[v]){
        //自己写的代码放在最上面,书上放这里,考虑到大部分时间在这个循环里,写这里更快
        if(hasCycle)return true
        let w=G[v][j][0]
        if(!marked[w]){
            from[w]=v
            testHasCycleAndDeleteOne_(w,marked,onStack,from)
        }else if(onStack[w]){
            hasCycle=true
            console.log('has cycle and delete--->',v,G[v].splice(j,1))
        }
    }
    onStack[v]=false
}

function DepthFirstOrder(V,G){				//拓扑排序
    for(let i=0;i<V;i++){
        if(!marked[i]){dfs(i,G)}
    }
}

function dfs(v,G){							//拓扑排序用的dfs
    marked[v]=true
    for(let j in G[v]){
        let w=G[v][j][0]
        if(!marked[w]){dfs(w,G)}
    }
    topoList.unshift(v)
}



//无环加权有向图的最短路径算法,属于这个算法的只有两个方法,其他都是测试用
function acyclicSP(topoList,G){
    for(let i in topoList){
        relax(topoList[i],G)
    }
}

function relax(v,G){						//放松点v的所有边
    for(let i in G[v]){
        let e=G[v][i]
        let w=e[0]
        let d=e[1]
        if(dist[v]+d<dist[w]){
            dist[w]=dist[v]+d
            from[w]=v
        }
    }
}

function dijkstra(v,G){
    var pq=[]
    var dist=[0]
    var from=[]
    let maxDist=Math.pow(2,31)-1
    for(let i=1;i<V;i++){
        dist.push(maxDist)
    }
    pq.push([0,0,0])
    while(pq.length>0){
        relaxDijkstra(deleteMin(pq),G,pq,dist,from)
    }
    return [from,dist]
}

function relaxDijkstra(e1,G,pq,dist,from){
    let v=e1[1]
    for(let i in G[v]){
        let e=G[v][i]
        let w=e[0]
        let d=e[1]
        if(dist[v]+d<dist[w]){
            dist[w]=dist[v]+d
            from[w]=v
            pq.push([v,w,d])                    
        }
    }
}

function getWay(v,from_){
    var ways=[]
    while(v!=0){
        ways.unshift(v)
        v=from_[v]
    }
    ways.unshift(0)
    return ways
}

//bellman-ford算法
function bf(start,G){
	var pq=[]
	var dist=[0]
	let maxDist=Math.pow(2,31)-1
	for(let i=1;i<V;i++){dist[i]=maxDist}
	var from=[]
    pq.push(start)
	while(pq.length>0){
		relax11(pq.pop(),G,from,dist,pq)
	}
	return [from,dist]
}

function relax11(v,G,from,dist,pq){
	for(let i in G[v]){
		let e=G[v][i]
		let w=e[0],d=e[1]
		if(dist[w]>dist[v]+d){
            dist[w]=dist[v]+d
			from[w]=v
			if(pq.indexOf(w)==-1){pq.push(w)}
		}
	}
}

var hasCycle=false
let V=99
var pq=[]
var dist=[0]
var from=[]
var marked=[]
var topoList=[]
let maxDist=Math.pow(2,31)-1
for(let i=1;i<V;i++){
    dist.push(maxDist)
}

let generateDirectedGraph=require('./wgraph').generateDirectedGraph
let generateEdges=require('./wgraph').generateEdges
let es=generateEdges(V,V*2,9,0)
var G=generateDirectedGraph(V,es,false)

DepthFirstOrder(V,G)

//检测是否有环,有删除一条边,直到无环为止
while(true){
    testHasCycleAndDeleteOne()
    if(!hasCycle){break}
    hasCycle=false
}

if(!hasCycle){
    for(let i=0;i<V;i++){console.log(i,':',G[i])}
    console.log('topoList-->',topoList)
    acyclicSP(topoList,G)
    //console.log(from,'\n',dist)
}
let res=dijkstra(0,G)
let fromDijkstra=res[0]
//console.log('fromDijkstra->',fromDijkstra)

//可能到一个点有多条最短路径,用getWay打印路径进行人工测试
for(let i=0;i<V;i++){
    if(from[i]!=fromDijkstra[i]){
    	//getWay可能出现死循环,dijkstra的负边导致start不经过的点也被relax,所以这些点的from最终是undefined,无法跳出v!=0的循环
        console.log(getWay(i,from),getWay(i,fromDijkstra),dist[i])
    }
}
console.log(JSON.stringify(from)==JSON.stringify(fromDijkstra))
console.log(JSON.stringify(dist)==JSON.stringify(res[1]))
let res1=bf(0,G)
console.log(JSON.stringify(from)==JSON.stringify(res1[0]))
console.log(JSON.stringify(dist)==JSON.stringify(res1[1]))

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值