Dijkstra及其队列优化
设计背景
Edsger Wybe Dijkstra
荷兰人
1972 Turing Award,
结构化程序设计语言之父,
成就有 goto有害论, Dijkstra算法。
过程模拟
-
定义集合P为已求出最短路径的顶点, Q为未求出最短路径的顶点
-
设起点为s,dis[v]表示从s到v的最短路径, pre[v]为v的前驱节点,用来输出路径。
-
初始化:
dis[v]=∞(v≠s);dis[s]=0; pre[s]=0;
for (i = 1; i <= n ; i++)
1.在集合Q中找一个顶点u使得dis[u]最小
2.u标记为已确定最短路径,从Q中删除,加 入P中
3.for 与u相连的每个顶点v
if(dis[u]+w[u][v]<dis[v]) { dis[v]=dis[u]+w[u][v];pre[v]=u; }
- 结束:dis[v]为s到v的最短距离;
pre[v]为v的前驱,用来输出路径。
(有图有真相)
以0点为源点,dist[i]为源点到顶点的最短路径。
步骤 | dist[1] | dist[2] | dist[3] | dist[4] | 已找到的集合 |
---|---|---|---|---|---|
第一步 | 8 | 1 | 2 | +∞ | {2} |
第二步 | 8 | 1 | 2 | 4 | {2,3} |
第三步 | 5 | 1 | 2 | 4 | {2,3,4} |
第四步 | 5 | 1 | 2 | 4 | {2,3,4,1} |
由于老韩在课上讲到了**质疑**的重要性!
那么
Emotion。发现你可能存在以下疑问
若A作为源点,与其邻接的只有B,C,D三点,
其dist[]最小时顶点为C,即就可以确定A→C为A到C的最短路。
但是我们存在疑问的是:是否还存在另一条路径使A到C的距离更小?
用反证法证明。
假设存在如上图的红色虚线路径,
使A→D→C的距离更小,
那么A→D作为A→D→C的子路径,
其距离也比A→C小,
这与前面所述“dist[]最小时顶点为C”矛盾,
故假设不成立。
因此这个疑问不存在。
根据上面的证明,
我们可以推断出,
Dijkstra每次循环都可以确定一个顶点的最短路径,
故程序需要循环n-1次。
(该部分内容借鉴大佬博客qwq)
完整代码
(这是一份喜欢卖萌的代码qwq)
#include<iostream> #include<cstdio> #include<cstring> #include<queue> //#include<qwq> #define INF 0x3fffffffffffffff #define ll long long #define maxn 10010 //#define QAQ qwq using namespace std; ll dis[maxn]; //result bool v[maxn]; //访问与否 int n,m,s; //n: //int QVQ; struct my_pair { int v; //to ll d; //data my_pair(int x,ll y) { v=x; d=y; //初始化 } bool operator < (my_pair r/*right*/) const { return d>r.d; } //运算符重载 }; //struct QVQ struct Edge { int to,next; //to=go,next=from ll val; //data }E[maxn*2]; int cnt; int h[maxn]; //head void add(int u,int v,ll d) { E[++cnt].to=v; E[cnt].val=d; E[cnt].next=h[u]; h[u]=cnt; } void dijkstra(int x) { memset(v,0,sizeof(v)); //将v数组初始化,使得里面没有点 for(int i=1;i<=n;i++) dis[i] = (i==x) ? 0 : INF; //将源点存储,标记权值为0 priority_queue <my_pair> pq; pq.push(my_pair(x,dis[x])); //将x,dis【x】存入my_pair对象中 while(!pq.empty()) { int r=pq.top().v; //search 顶点 ll rdis=pq.top().d; //search data pq.pop(); //弹出第一个点 if(v[r]) continue; // 访问过的点拒绝访问 else v[r]=true; //立flag标记访问与否 for(int i=h[r];i;i=E[i].next) //从顶点访问到完 { int y=E[i].to; //y点是这条边到达的点 if(rdis+E[i].val<dis[y]) { dis[y]=rdis+E[i].val; pq.push(my_pair(y,dis[y])); } } } } //int main QAQ int main() { cout<<"q请输入点数:"<<endl; cin>>n; cout<<"w请输入边数:"<<endl; cin>>m; cout<<"q请输入起点:"<<endl; cin>>s; cout<<"QAQ请输入每条边的两点及权值:"<<endl; for(int i=1;i<=m;i++) { int u,v; ll d; cin>>u>>v>>d; add(u,v,d); add(v,u,d); } dijkstra(s); cout<<"Orz这是结果:"<<endl; for(int i=1;i<=n;i++) cout<<"1->"<<i<<" "<<dis[i]<<endl; return 0; }
结果表示:
时间复杂度
众所周知,Dijkstra算法的时间复杂度为
而Dijkstra在优先队列的的优化下时间复杂度可达O(nlogn)O(nlogn)O(nlogn);
(毕竟Emotion。对时间复杂度的计算有些问题,所以就说这么多啦qwq)
emmm
就这么结束吧
真是不敢相Emotion的第一篇博客是关于Dijkstra的QAQ
最后附上大佬博客qwq大佬在这里!