Dijkstra

2 篇文章 0 订阅

简单介绍

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959
年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。

问题的提出:给定一个带权无向图 G 和源点 v0,求 v0 到 G 中其他每个顶点的最短路径。限定各边上的权值非负

算法分析&思想讲解:

从起点到一个点的最短路径一定会经过至少一个“中转点”
(例如下图1到5的最短路径,中转点是2。特殊地,我们认为起点1也是一个“中转点”)。
显而易见,如果我们想求出起点到一个点的最短路径,那我们必然要先求出中转点的最短路径(例如我们必须先求出点2 的最短路径后,才能求出从起点到5的最短路径)。
换句话说,如果起点1到某一点V0的最短路径要经过中转点Vi,那么中转点Vi一定是先于V0被确定了最短路径的点。
为求得这些最短路径,Dijkstra 提出按路径长度的递增次序,逐步产生最短路径的算法。
我们把点分为两类,一类是已确定最短路径的点,称为“白点”,另一类是未确定最短路径的点,称为“蓝点”。如果我们要求出一个点的最短路径,就是把这个点由蓝点变为白点。从起点到蓝点的最短路径上的中转点在这个时刻只能是白点。
Dijkstra的算法思想,就是一开始将起点到起点的距离标记为0,而后进行n次循环,每次找出一个到起点距离dis[u]最短的点u,将它从蓝点变为白点。随后枚举所有的蓝点vi,如果以此白点为中转到达蓝点vi的路径dis[u]+w[u][vi]更短的话,这将它作为vi的“更短路径”dis[vi](此时还不确定是不是vi的最短路径)。
就这样,我们每找到一个白点,就尝试着用它修改其他所有的蓝点。中转点先于终点变成白点,故每一个终点一定能够被它的最后一个中转点所修改,而求得最短路径。
在这里插入图片描述
以这张图为例
首先求出长度最短的一条最短路径:0 -> 2,
长度为 5(2 号点).顶点 2 的最短路求出来之后,
0 到其他顶点的最短路径长度有可能要改变,比如 0 -> 1 从原来的 ∞ 变成了 20, 0 -> 5 从原来的 ∞ 变成了 12.
这样次短的最短路就是 12(5 号点).
5 的最短路求出后,0 到其他点的最短路还会发生改变。比如 0 -> 3 从原来的 30 变为了 22,0 -> 4 也从 ∞ 变为了 28。这时第三短的最短路就确定了,是 20(1 号点).
之后再依次确定第四短的最短路是 22(3 号点).
第五短的最短路是 28 (4 号点)

普通模版

时间复杂度O(n2)

void Dijkstra()
{
	memset(dis,127,sizeof(dis));
	memset(exist,0,sizeof(exist));
	int x,y,z;
	int i,j;
	int cmin;
	dis[1]=0;
	for(j=1;j<=n;j++) {
		cmin=INF; 
		for(i=1;i<=n;i++) {
			if(exist[i]) continue; //不考虑已经选的点 
			if(dis[i]<cmin) { cmin=dis[i]; x=i; } //选出dis最小的点 
		}
		exist[x]=true;//选中
		for(i=one[x];i;i=Next[i]) {//scan all the edges;
			y=ver[i]; z=edge[i];
			if(dis[y]>dis[x]+z)    //improve node y;
				dis[y]=dis[x]+z;
		} 
	}
	return;
}

堆优化模板

时间复杂度O((n+m)logn)

核心代码

priority_queue< pair<int,int> > q;
		//大根堆(优先队列),pair的第二维为节点编号;
		//pair的第一维为dis的相反数,利用相反数变成小根堆;
		//默认对pair的第一维排序; 
void Dijkstra()
{
	memset(dis,127,sizeof(dis));
	memset(exist,0,sizeof(exist));
	while(q.size()) q.pop();
	int u,x,y,z;
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()) {
		x=q.top().second; q.pop();
		if(exist[x]) continue;//已经被选中 
		exist[x]=true;//选中
		for(int i=one[x];i;i=Next[i]) {//scan all the edges;
			y=ver[i]; z=edge[i];
			if(dis[y]>dis[x]+z) {  //relax node y;
				dis[y]=dis[x]+z;
				q.push(make_pair(-dis[y],y)); //push node y;
			}
		} 
	}
	return;
}

完整代码

#include<bits/stdc++.h>
using namespace std;
priority_queue< pair<int,int> > q;
		//大根堆(优先队列),pair的第二维为节点编号;
		//pair的第一维为dis的相反数,利用相反数变成小根堆;
		//默认对pair的第一维排序; 
const int MAXN=10000*2+5;
const int MAXM=10000+5;
int dis[MAXN];
bool exist[MAXN];
int one[MAXN],ver[MAXN],edge[MAXN],Next[MAXN],adj[MAXN];
int n,m;
int tot=0;
/*输入n, m,表示 n个城市和 m条路;
接下来m行,每行a b c,表示城市a与城市b有长度为c的路。*/
void add(int a,int b,int c)//from a to b;
{
	tot++;
	if(one[a]==0) one[a]=tot;
	Next[adj[a]]=tot; adj[a]=tot;
	edge[tot]=c;
	ver[tot]=b;
	return;
}
void Init()
{
	int i,j;
	int a,b,c;
	cin>>n>>m;
	for(i=1;i<=m;i++) {
		scanf("%d%d%d",&a,&b,&c);
		add(a,b,c); //把一条无向边当两条有向边存
		add(b,a,c);
	}
	return;
}
void Dijkstra()
{
	memset(dis,127,sizeof(dis));
	memset(exist,0,sizeof(exist));
	while(q.size()) q.pop();
	int u,x,y,z;
	dis[1]=0;
	q.push(make_pair(0,1));
	while(!q.empty()) {
		x=q.top().second; q.pop();
		if(exist[x]) continue;//已经被选中 
		exist[x]=true;//选中
		for(int i=one[x];i;i=Next[i]) {//scan all the edges;
			y=ver[i]; z=edge[i];
			if(dis[y]>dis[x]+z) {  //relax node y;
				dis[y]=dis[x]+z;
				q.push(make_pair(-dis[y],y)); //push node y;
			}
		} 
	}
	return;
}
int main()
{
//	freopen("1.in","r",stdin);
	Init();
	Dijkstra();
	if(dis[n]!=dis[0]) cout<<dis[n];
	else cout<<"-1";
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值