算法总结—最短路径算法

针对 优先级队列优化过的dijkstra算法 以及 SPFA算法 进行讨论

一号选手:优先级队列优化过的dijkstra算法 (本质:bfs[ 队列改为优先级队列 ] + dis[]数组记录到达各点的距离 )

先贴出该算法的实现模板:

void dijkstra(int start) {
	for(int i=1;i<=T;i++) {
		dis[i]=inf;
	}
	dis[start]=0;
	
	priority_queue<node> pq;
	node nd;//临时存储变量使用 
	nd.at=start,nd.len=0;
	pq.push(nd);
	
	while(!pq.empty()) {
		node fa=pq.top(); pq.pop();	
		for(int i=0;i<mp[fa.at].size();i++) {
			node ch=mp[fa.at][i];
			if(fa.len+ch.len<dis[ch.at]) {
				dis[ch.at]=fa.len+ch.len;
				nd.at=ch.at,nd.len=fa.len+ch.len;
				pq.push(nd);
			}
		}
	}
} 

1、在没有负权边的情况下求某个点到其他所有点的距离

由于没有负权边,队列的top返回当前路径中最短的一条路径的可达点,那么到达该点的路径一定最短,

所以相同的点不会第二次进入队列,因为这行代码

fa.len+ch.len<dis[ch.at]

点评:耗时非常稳定,时间复杂度为elogv,在情况1下推荐优先使用。

2、在没有负权边的情况下求某个点到一个指定点的距离

由于没有负权边,所以可以在目标点进入队列的时候直接break跳出,即可以得到最短路径。

点评:在好的情况下可以快速找到结果,最坏情况下也不会耗时超过elogv,情况2下推荐优先使用。

3、有负权边的情况下求某个点到其他所有点的距离

由于存在负权边,队列的top不再是最短路径了,因为不知道哪里冒出一条负权边可能会打破原先的最短情况。

所以同一个点可能会入队列多次,并且一定要等到队列为空才可以跳出,否则dis【i】里的答案不一定正确的。

点评:耗时高昂,在情况3下,非常不推荐使用。

4、在有负权边的情况下求某个点到一个指定点的距离

由于存在负权边,所以与情况3一样,必须要等到队列为空才可以跳出。

点评:非常不推荐使用,即耗时高昂又显得弱智。

二号选手:SPFA算法 (本质:bfs[ 加一个数组判断点是否在队列内 ] + dis[]数组记录到达各点的距离 )

先贴出该算法的实现模板

void spfa(int start) {
	for(int i=0;i<=T;i++) {
		dis[i]=inf,has[i]=0;
	}
	dis[start]=0;
	
	deque<int> dq;
	dq.push_back(start);
	
	while(!dq.empty()) {
		int fa=dq.front(); dq.pop_front(); has[fa]=0;
		
		for(int i=0;i<mp[fa].size();i++) {
			node ch=mp[fa][i];
			if(dis[fa]+ch.len<dis[ch.at]) {
				dis[ch.at]=dis[fa]+ch.len;
				if(!has[ch.at]) {
					has[ch.at]=1;
					dq.push_back(ch.at);	
				}
			}
		}
		
	}
}

是否有负权边对SPFA算法不影响,直接讨论存在负权边的情况。

1、求某个点到其他所有点的最短路径

算法效率听说不错,但是非常不稳定,算法证明也有问题,自行百度SPFA算法。

点评:听说不错,实战也确实非常优秀,特别是在有负权边的情况下。

2、某个点到一个指定点的距离

这个与情况1一样,不管是否存在负权边,一定要等到队列为空才可以跳出,否则答案不一定正确。


总结:在没有负权边的情况下优先考虑dijkstra算法,比较稳定,当然SPFA也非常优秀。

有负权边的情况下那就使用SPFA算法,总体来说SPFA选手胜出。

最后说明一点, 没经过 优先级队列优化的dijkstra算法不能处理负权边的情况。

最最后,贴出经过 SLF优化 和 LLL优化的 SPFA算法,但是在实测中LLL优化反而增加了耗时。

测试例子是http://lx.lanqiao.cn/problem.page?gpid=T22

代码模板如下:

void spfa(int start) {
	for(int i=0;i<=T;i++) {
		dis[i]=inf,has[i]=0;
	}
	dis[start]=0;
	
	deque<int> dq;
	dq.push_back(start);
	
	int sum=0,cnt=1; //LLL优化需要使用的两个变量 
	
	while(!dq.empty()) {
		
		while(dis[dq.front()]*cnt>sum) {//LLL优化 
			dq.push_back(dq.front());
			dq.pop_front();
		}
		
		int fa=dq.front(); dq.pop_front();
		has[fa]=0;
		sum-=dis[fa]; cnt--;
		
		for(int i=0;i<mp[fa].size();i++) {
			node ch=mp[fa][i];
			if(dis[fa]+ch.len<dis[ch.at]) {
				dis[ch.at]=dis[fa]+ch.len;
				if(!has[ch.at]) {
					has[ch.at]=1;
					if(dis[ch.at]<dis[dq.front()]) {//SLF优化 
						dq.push_front(ch.at);
					}else {
						dq.push_back(ch.at);
					}
					sum+=dis[ch.at]; cnt++;
				}
			}
		}	
	}
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值