最短路径算法——Dijkstra

什么是单源最短路算法

第一次学习这个算法时,老师告诉我们,这是一个单源最短路算法

那问题来了,什么是“单源最短路”呢?

其实也很好理解

最简单的Floyd作比较 →   Floyd此处传送

为什么Floyd慢?

因为ta有3层循环

为什么要3层循环呢?

因为ta计算的是任意两个点之间的最短路径

而当你学到后面就会知道Dijkstra的时间复杂度为O(n*n)

                                        Floyd的时间复杂度为O(n*n*n)

这就是因为Dijkstra是“单源最短路”算法(感觉我说了一堆废话

综上所述(我也不知道上面讲了些啥):

                单源最短路是只计算一个点到其他点的最短路径

举个简单的例子

这是一个有向图

让我们求1点到各点(即2 3 4 5)的最短的距离

如果要用Floyd做,直接3层循环就可以了,但是我很无聊,非不用Floyd做,用Dijkstra做

然后我们来分一下步骤:

1.首先,我们定义一个数组dis[6],代表1到所有点的距离,依次是1到1 2 3 4 5的距离,先把他们都     赋值成无穷大

//2.数组定义完之后,要放点东西啊

//2.被篮圈圈住的三个点,都是1 一次性就能到达的(即,1出度对应的点

2.我们就先把这3个点的dis更新为1到他们的边权值,如下图   ↓

***注:这里dis数组里的值并不是最短路径,也不是最终答案,dis数组会一直变化,直到把所有边             、所有点都操作一遍后才是最终答案

***注:这里1的无穷大只是为了区分,实际代码里自己到自己的距离是0,所以本文章dis[1]的值实际上都是0

3.刚刚计算的是1的出度对应的点,现在再看2的出边

 

//4.然后我们发现从2出发只能到达5,所以把dis[5]的值更新

*注:dis数组里的所有值都是从出发点开始的路径,也就是指此题中的dis值都是1到一个点的值

4.然后就可以更新成这个样子   ↓

 //5.2的出边我们算完了,就计算3的

//5.然后发现3能到达2和5     ↓

//5.但2和5的dis都有值了

//5.那么就比较经过3的dis和目前dis数组里的值,取小的值

//5.我们可以计算出1→3→2的dis为5,比原来的dis[2](10)要小,更新

                              1→3→5的dis为3,比原来的dis[5](15)要小,更新

5.dis数组值更新如下   ↓

 //6.以此类推,再计算4出边对应的点

//6.具体过程和前面2、3的过程一样

//6.计算3、5经过4的dis,再作比较

6.更新如下  ↓

//6.因为从经过4的dis值比原来的dis[3],dis[5]都小,所以不用更新

7. 5没有对应的出边和点,不用操作dis

8.所有点都遍历过一遍了,此时的dis数组储存的也都是1到其他点最短路径

9.输出(或者做别的操作,看题目),程序圆满结束

对了,还有一个专有名词——松弛

简单说一下

其实计算并更新dis的值就是松弛(其实知不知道都一样

(够简单的吧

是时候上代码了

题目

计算点1到其他点(2 3 4 5)的最短路径,依次输出

#include <bits/stdc++.h>
using namespace std;
int e[1005][1005]; //存图 
int dis[1005];
int n,m; //n代表点数,m代表边数 
const int inf=2147483647;
int flag[1005]; //标记数组,保证每个点都可以作为经行dis计算的初始点 
int minn=inf;
int uu;

int main()
{
	cin>>n>>m;
	
	//初始化图 
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			if(i==j) e[i][j]=0;
			else e[i][j]=inf;
		} 
	
	//输入
	for(int i=1;i<=m;i++)
	{
		int u,v,w;
		//u到v有一条长度w的边(有向图
		cin>>u>>v>>w;
		e[u][v]=w; 
	}
	
	//初始化dis数组
	for(int i=1;i<=n;i++)
		dis[i]=e[1][i];
		
	//初始化标记数组
	memset(flag,0,sizeof(0));
	flag[1]=1; //从1开始遍历
	
	//Djakarta核心
	for(int i=1;i<=n-1;i++) //i代表现在遍历的起始点编号 
	{
		minn=inf;
		for(int j=1;j<=n;j++) //j代表从1经过出发点能够到达j 
		{
			if(flag[j]==0 && dis[j]<minn)
			{
				minn=dis[j]; //更新最小值 
				uu=j; //记录能够经行转换长度的点编号 
			}
		}
		for(int j=1;j<=n;j++)
		{
			if(e[uu][j]<inf) //判断有边可走 
			{
				if(dis[j]>dis[uu]+e[uu][j]) //经行松弛 
				   dis[j]=dis[uu]+e[uu][j];
			}
		}
	}
	
	for(int i=1;i<=n;i++)
		cout<<dis[i]<<' ';
	
	return 0;
}

看到这里,点个赞吧(逃

Floyd中,我们最后留的一道题Floyd显然会超时,相信你学习了Dijkstra后,一定能解决了吧

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值