Dijkstra算法

导言

Dijkastra以其优秀的复杂度O(mlogm)被我们所熟知。成为学习最短路的必备算法。


基本信息

迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959年提出的,因此又叫狄克斯特拉算法。是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。

别名:Dij,迪杰斯特拉算法。


算法介绍

Dijkstra运用贪心策略,每次选择离此点最近的下一个点进行松弛操作——保证了操作的有效性。

主要准备工作:

  • int/long long型的dis数组,统计起点到各个点的最短距离。
  • bool型的vis数组,为已经确定最短距离的点打上标记,避免重复运算。
  • 邻接矩阵或者邻接表(邻接矩阵建议用vector双重嵌套)。
  • 按照题意读入,并将边的关系加入邻接矩阵或邻接表。

算法主要步骤:

  • 将dis数组全部重置为INT_MAX(就最大值)——memset和for初始化都可。
  • dis[起点]=0;

下面开始,在for循环内:for(int i=1;i<=n-1;i++)

  • 设置一个变量,for循环枚举与i相连的点的距离最小值(被选中过要跳过),并记录下节点编号。
  • 将最小值最小的点打上标记,杜绝重复运算。vis[相应的节点] = 1;
  • 将选中的点相连的点进行松弛操作。

根据这样的操作,我们可以保证每次选中的点 t 不可能再被松弛:我们令 dis[i] 为 i 到起点 s 的已知最短距离,若节点 t 已经被选中过,说明 dis[t] 一定小于  ( now 为当前选中进行松弛操作的节点),不然 now 应该比 t 先被选中。所以一个节点被选中后,说明其 dis 值已经确定,不会在进行更改。

代码实现:

for(int i=1;i<=n-1;i++){
	long long t, Min = 0x7fffffff/2;
	for(int j=1;j<=n;j++){//寻找距离起点最近的节点
		if(vis[j]) continue ; //被选中过则跳过
		if(dis[j] < Min){
			Min = dis[j];//更新最小值
			t = j;//更新t节点
		}
	}
	dis[t] = 1;//标记
	for(int j=0;j<v[t].size();j++){//枚举与t相邻的节点
		long long k=v[t][j].v;
		if(dis[k] > dis[t]+v[t][j].w){//松弛操作
			dis[k] = dis[t]+v[t][j].w;
		}
	}
}

算法优化

这时候,大家肯定会发现——这,这时间复杂度……。

呵呵,不用惊讶,未优化前的Dijkstra就是比Floyd稍好一点的算法而已——O(n2)的复杂度。

所以我们要:提高找到最小值的效率——就用堆!!!

现在我们共有两种选择:1. 直接声明小根堆:priority_queue<int, vector<int>, greater<int>>;——但我们也需要节点编号啊!没有关系,我们可以套用pair<int, int> ,以下为示例:

typedef pair<int,int> PII;
priority_queue<PII, vector<PII>, greater<PII>> heap;
//或者
struct node{
    int weight, site;
}
priority_queue<node, vector<node>, greater<node>>;

2.使用重载运算符

struct priority
{
    int ans;
    int id;
    bool operator <(const priority &x)const
    {
        return x.ans<ans;
    }
};
priority_queue<priority> q;

优化后,我们只需要push({x,x}) 或 push(makepair(x,x)) —— 时间复杂度直线下降!


代码示例

小根堆实现
 #include <vector>
#include <queue>
#include <limits>
using namespace std;
 
typedef pair<int, int> PII;
const int N = 1e5 + 10;
int h[N], e[N], ne[N], w[N], idx;
int dist[N];
bool st[N];
 
void add(int a, int b, int c) {
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
 
void dijkstra(int n, int start) {
    priority_queue<PII, vector<PII>, greater<PII>> heap;
    fill(dist, dist + n, numeric_limits<int>::max());
    dist[start] = 0;
    
    heap.push({0, start});
    while (heap.size()) {
        auto t = heap.top();
        heap.pop();
        int ver = t.second, distance = t.first;
        if (st[ver]) continue;
        st[ver] = true;
        for (int i = h[ver]; i != -1; i = ne[i]) {
            int j = e[i];
            if (dist[j] > distance + w[i]) {
                dist[j] = distance + w[i];
                heap.push({dist[j], j});
            }
        }
    }
}
 
// 使用示例
int main() {
    // 初始化图
    int n = 5; // 节点总数
    memset(h, -1, sizeof h);
    add(0, 1, 1);
    add(0, 2, 2);
    add(1, 2, 1);
    add(1, 3, 2);
    add(2, 4, 1);
    add(3, 4, 2);
 
    // 运行Dijkstra算法
    dijkstra(n, 0);
 
    // 输出结果
    for (int i = 0; i < n; ++i) {
        printf("起点到%d的最短路径长度为:%d\n", i, dist[i]);
    }
    return 0;
}
重载运算符实现
 #include<cstdio>
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
inline int read()
{
	char ch=getchar(); 
	int x=0,f=1;
	while((ch>'9'||ch<'0')&&ch!='-')
		ch=getchar();
	if(ch=='-')
	{
		f=-1;
		ch=getchar();
	}
	while('0'<=ch&&ch<='9')
	{
    	x=x*10+ch-'0';
    	ch=getchar();
	}
	return x*f;
}
int mod=100003;
int n,m,x,y,tot=0;
const int N=1000005,M=4000005;
int head[N],to[M],nxt[M],d[N],ans[N];
bool p[N];
priority_queue< pair< int,int > > q;
void add(int x,int y)
{
	to[++tot]=y;
	nxt[tot]=head[x];
	head[x]=tot;
}
int main()
{
	n=read();m=read();
	for(int i=1;i<=m;i++)
	{
		x=read();y=read();
		add(x,y);
		add(y,x);
	}
	for(int i=1;i<=n;i++)
	{
		d[i]=1e9;p[i]=0;
	}
	d[1]=0;ans[1]=1;
	q.push(make_pair(0,1));
	while(q.size())
	{
		x=q.top().second;
		q.pop();
		if(p[x])	continue;
		p[x]=1;
		for(int i=head[x];i;i=nxt[i])
		{
			y=to[i];
			if(d[y]>d[x]+1)
			{
				d[y]=d[x]+1;
				ans[y]=ans[x];
				q.push(make_pair(-d[y],y));
			}
			else if(d[y]==d[x]+1)
			{
				ans[y]+=ans[x];
				ans[y]%=mod;
			}
		}
	}
	for(int i=1;i<=n;i++)
		printf("%d\n",ans[i]);
	return 0;
}

谢谢大家的阅读——最短路题题AC!!!

 

posted on 2024-09-09 15:42  pengbubu  阅读(200)  评论(0)    收藏  举报

导航