堆优化
堆优化的主要思想就是使用一个优先队列(就是每次弹出的元素一定是整个队列中最小的元素)来代替最近距离的查找,用邻接表代替邻接矩阵,这样可以大幅度节约时间开销。
在这里有几个细节需要处理:
首先来讲,优先队列的数据类型应该是怎样的呢?
我们知道优先队列应该用于快速寻找距离最近的点。由于优先队列只是将最小的那个元素排在前面,因此我们应该定义一种数据类型,使得它包含该节点的编号以及该节点当前与起点的距离。
我们应该在什么时候对队列进行操作呢?
队列操作的地方,首先就是搜索刚开始,要为起点赋初始值,此时必须将起点加入优先队列中。该队列元素的节点编号为起点的编号,该节点当前与起点的距离为0
那么如果一个节点到起点的最短距离通过其他的运算流程发生了变化,那么如何处理队列中的那个已经存入的元素?
事实上,你不需要理会队列中的元素,而是再存入一个就行了。因为如果要发生变化,只能将节点与起点之间的距离变得更小,而优先队列恰好是先让最小的那个弹出。
因此,轮到某一个队列元素弹出的时候,如果有多个元素的节点编号相同,那么被弹出的一定是节点编号最小的一个。等到后面再遇到这个节点编号的时候,我们只需要将它忽略掉就行了。
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
using namespace std;
const int MAX_N = 2e5+7;
struct node{
int v,w;
};
priority_queue<pair<int,int> > q;//pair的第一个元素表示从源点到这个点的最短距离,第二个元素表示点的编号
int vis[MAX_N],dis[MAX_N];
vector <node> vec[MAX_N];
void dij(int x){
memset(dis,0x3f3f3f3f,sizeof dis);
dis[x] = 0;
q.push(make_pair(0,x));
while(!q.empty()){
int now = q.top().second;
q.pop();
if(vis[now]) continue;
vis[now] = 1;
for(int i = 0; i < vec[now].size(); i++){
int x = vec[now][i].v;
int z = vec[now][i].w;
if(dis[x] > dis[now] + z){
dis[x] = dis[now] + z;
q.push(make_pair(-dis[x], x));//优先队列默认从大到小排序,而且按pair的第一个元素进行排序,所以我们需要距离小的在前面,就应该在d前加一个负号
}
}
}
return ;
}
int main(){
int n,m,s;
cin >> n >> m >> s;
for(int i = 0; i < m; i++){
int u,v,w;
scanf("%d%d%d", &u,&v,&w);
node nod;
nod.v = v;
nod.w = w;
vec[u].push_back(nod);
}
dij(s);
for(int i = 1; i <= n; i++){
printf("%d ",dis[i]);
}
return 0;
}