最短路dijkstra朴素及其优化
dijkstra算法:
图的存储:
稀疏图可以用邻接表来存
稠密图可以用邻接矩阵来存
这个算法基于稠密图,所以应该用邻接矩阵来存
算法思想:
创建一个集合s表示所有确定下来最短距离的点
用t来表示没有确定下来最短距离的点
用完t之后,把t放入s
然后一次次的迭代更新,保证每一个点到起点的距离都是最短距离
举例:
此时到起点距离最短的点就是dist[1]这个点,距离为0
然后开始迭代检查更新:
从1号点可以到达2号和3号,2号到起点的最短距离为dist[1]+2等于2小于正无穷,可以被更新为2
3号到起点的距离为dist[1]+4等于4等于4小于正无穷,3号点可以被更新为4
但此时只是迭代到1号点,刚刚的2,3号点只是被更新而已并没有确定下来,因此更新完之后,s集合只有1号点,2,3继续待定
第二次迭代:再次没有确定的点中找到最小值
此时的2号点(没有确定)到起点是最短的,可以把2号点放入集合s中了
再接着,用2号点来更新它到其他点与起点的最短路径:
2号点只可以到3号点
3号此时的距离是4,2号到起点的距离是2,从2号到3号:3号到起点的最短距离:2号到起点的最短距离+1也就是dist[2]+1等于3小于4,所以此时需要更新
再下一轮迭代:
就剩一个3号点了,那此时的3号点就是之前更新过的3,直接加入s集合
时间复杂度O(n*n)
对于代码的解释我下面给出注释了:
#include<iostream> #include<cstring> using namespace std; int n,m; const int N=510; int g[N][N],dist[N]; bool st[N]; int dijkstra(){ memset(dist,0x3f,sizeof(dist));//除了第一个,其他的都初始化为正无穷好方便取值 dist[1]=0; for(int i=0;i<n;i++)//n次迭代,其实可以到n-1次,最后一次可以省略 { int t=-1; for(int j=1;j<=n;j++) if((!st[j])&&(t==-1||dist[j]<dist[t]))//t==-1设置是为了能找到第一个没被标记的点 //用这个点作为基准然后与后面的进行比较,找出全部未被标记且路径最短的点 t=j;//更新1到n中路径最小的那个点且未被标记 st[t]=true; for(int j=1;j<=n;j++) dist[j]=min(dist[j],dist[t]+g[t][j]);//更新 } if(dist[n]==0x3f3f3f3f) return -1; else dist[n]; } int main(){ memset(g,0x3f,sizeof(g));//初始化无穷大,以便于下面重边取最小值的比较 cin>>n>>m; while(m--) { int a,b,z; cin>>a>>b>>z; g[a][b]=min(z,g[a][b]);//可能会有重边,因为求的是最短路径,只要可以连通,取最小值就可以了,其他的可以省略 } int t=dijkstra(); cout<<t<<endl; return 0; } /* 先初始化 接着求出未在s中的最短的 放入s 更新 主要就是这个四步 */
以上算法的复杂度是O(n*n),下面介绍堆排序优化的dijkstra算法:
这个复杂度只需要O(mlogn)
其实算法的思想还是基于上面的,但用到了堆的数据结构
堆的可以执行以下操作:找出最小的,修改。删除
也可以用手写堆和STL里面的优先队列来维护这个堆
下面的主要注释在代码里面,思想与上面差不多哦
#include<iostream> #include<cstring> #include<queue> using namespace std; typedef pair<int,int> PAII; int n,m; const int N=150000;//在边是点的次方时是稠密图用邻接矩阵来存,二者差不多用邻接表来存,这里是稀疏图选择邻接表 int h[N],e[N],ne[N],idx,w[N];//w代表权重 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++; } int dijkstra(){ memset(dist,0x3f,sizeof(dist)); dist[1]=0; priority_queue<PAII,vector<PAII>,greater<PAII>> heap;//小根堆,第一个为最小的 heap.push({0,1});//堆里面存的是点,一定是第一个为距离,第二个为点的序号,pair排序默认了先按第一个进行排序,那肯定是按照距离排序 //下面是找到未被标记的距离最短的点 while(heap.size()) { auto t=heap.top(); heap.pop(); int dis=t.first,ver=t.second; if(st[ver]) continue;//如果此时的点被标记过则跳过 else st[ver]=true;//否则就使用这个点,因为堆里面的数据都是经过排序过的,所以只要遇到没被标记过的,那就直接是距离最短的那个点 for(int i=h[ver];i!=-1;i=ne[i])//更新 { int j=e[i];//所对应点的坐标 if(dist[ver]+w[i]<dist[j])//dist表示这个点到起点的距离 { dist[j]=dist[ver]+w[i]; heap.push({dist[j],j});//把新更新过的点但没有标记哦放入堆 } } } if(dist[n]==0x3f3f3f3f) return -1; else return dist[n]; } int main(){ cin>>n>>m; memset(h,-1,sizeof(h));//把邻接表初始化 while(m--) { int a,b,z; cin>>a>>b>>z; add(a,b,z);//这里可以不用像邻接矩阵那样考虑重边,可以被更新取最小值,只是堆里面会有很多冗余 } int t=dijkstra(); cout<<t<<endl; return 0; } /* 其实堆的作用就是代替朴素版的找未被标记的距离最近的点 ,其他的不变 用不着想的多高大上... */
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具