最短路径算法 Dijstra、Floyd、Bellman-Ford

算法课这个女人居然还要弄期中考试。。每次上完课都觉得怀疑人生,早知道从第一节课开始就应该自己在下面自学了(卑微)。
今天回顾一下几个最短路径算法吧。之前课设写过,但是发现已经忘记了。。(我除了菜什么都没有)

最短路径算法

最短路径算法常见的有Dijstra和Floyd。
Dijstra采用的是贪心策略,常用于解决单源点问题,贪心策略是不能回头的,所以Dijstra不能用于有负权的图(因为在此算法中,前提是更长的路径是短的路径派生来的,如果有负权的话,很可能在加上了一条新的边之后,路径变短了,前提也就不成立了)。
Floyd算法是一个经典的动态规划算法,用于求解所有点对之间的最短距离。基本步骤就是找子问题,写递推关系式(听起来挺简单是吧)。
再有就是Bellman-Ford算法,也是解决单源点问题,采用的是动态规划策略,但是可以用于处理有负权边的图。

Dijstra算法

我们总是找当前可见范围内的最短路径。
这里定义两个集合,T集合中存放已经确定下来的点,P集合中存放还未确定的点,再定义一个数组Dis,存放当前源点v到其他点的最短路径。
我们拿这个超丑的图来分析。
在这里插入图片描述
选v1作为源点。
(1)初始时
Dis=[0,9,∞,∞,14,2]
T={v1}
P={v2,v3,v4,v5,v6}
可知离v1最近的点是v6,把v6加入到T中,并从P中删除。
T={v1,v6}
P={v2,v3,v4,v5}
(2)现在v6也在我们的可见范围内了,也就是说跟v6相连的点可以纳入考虑了,我们需要更新Dis,看看是否有到v1的更短的路径。
v1-v2,直达是9,从v6中转过去是∞,不更新;
v1-v3,原来是∞,现在从v6过去是13,更新Dis;

经过这一轮更新,Dis={0,9,13,12,11,2},从P中选一个距离v1最近的点,也就是v2。
T={v1,v2,v6}
P={v3,v4,v5}
(3)现在v2也加入了我们的可见范围。
更新一轮的结果是Dis={0,9,13,12,11,2},选v5。
T={v1,v2,v5,v6}
P={v3,v4}
(4)…
重复以上步骤直到P集合空了,就可以得到最终的Dis。

源代码:

	public void minPath(int v){	
		for(int i=1;i<=matrix.ver_num;i++){
			dis[i] = new Dis();
			dis[i].value=matrix.arc[v][i];
			
		}
		dis[v].visited=true;
			
		int count=1;
		while(count<=matrix.ver_num){
			int temp_min=8888;
			int temp_index=v;
			for(int j=1;j<=matrix.ver_num;j++){
				if(dis[j].visited==false && dis[j].value<temp_min){
					temp_min=dis[j].value;
					temp_index=j;
				}
			}
			dis[temp_index].visited=true;
			count++;
			for(int j=1;j<=matrix.ver_num;j++){
				if((dis[temp_index].value+matrix.arc[temp_index][j])<dis[j].value && dis[j].visited==false && matrix.arc[temp_index][j]!=8888){
					dis[j].value=dis[temp_index].value+matrix.arc[temp_index][j];
				}
			}
			
		}
	}
Floyd算法

这个算法的过程,简单粗暴地说,就是往两个点中间加点的过程。这里是定义了dis数组来记录两点之间的距离,通过更新dis数组找到所有点对之间的最短距离。感觉不知道能多说什么,对这个问题的理解可能还不是很透彻。

	public void minFloyd(){
		int select;
		for(int temp=1;temp<=matrix.ver_num;temp++){
			for(int i=1;i<=matrix.ver_num;i++){
				for(int j=1;j<=matrix.ver_num;j++){
					select=(dis[i][temp]==8888 || dis[temp][j]==8888)?8888:(dis[i][temp]+dis[temp][j]);
					if(dis[i][j]>select){
						dis[i][j]=select;
					}
				}
			}
		}
	}
Bellman-Ford算法

相比之下,bellman-ford感觉更好理解一点。
基本步骤是这样的:
(1)定义一个数组minDis来记录目前每个点到源点v的最短距离,minDis[v]初始化为0,其他的都初始化为无穷大。
(2)对每一条边做松弛(详见代码注释),若存在到源点的更短距离,更新minDis。
(3)检测是否有负权回路,因为有负权回路的话,就可以在负权回路上一直走,距离达到无穷小。

	public int minBF(int v){
		for(int j=1;j<matrix.ver_num;j++){
			for(int i=1;i<=matrix.edge_num;i++){
				int begin=matrix.edge[i].begin;
				int end=matrix.edge[i].end;
				int value=matrix.edge[i].value;
				//松弛,对于每一条边,如果当前x到源点v的距离在加上weight(x,y)之后会比当前y到源点的距离小
				//说明找到了一条更短的路径,则更新y到源点的距离
				if(minDis[begin]!=8888 && minDis[begin]+value<minDis[end]){
					minDis[end]=minDis[begin]+value;
				}
			}
		}
		//判断是否有负权回路
		int flag=0;
		for(int i=1;i<=matrix.edge_num;i++){
			int begin=matrix.edge[i].begin;
			int end=matrix.edge[i].end;
			int value=matrix.edge[i].value;
			//我的理解是这样的,在进行上面的操作之后,所有距离都应该是最小的了
			//若存在负权回路,那么就总是可以找到更小的距离,以此判断
			if(minDis[begin]!=8888 && minDis[begin]+value<minDis[end]){
				flag=1;			
				break;
			}
		}
		return flag;
	}

之后再看一下这几个算法的优化,希望可以填坑。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值