非加权无向图—Floyd算法的优化
非加权无向图—Floyd算法的优化
PS:此算法的优化只针对非加权无向图,因为优化是利用了无向图邻接矩阵的对称性。
经典实现
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
}
}
}
}
经典算法简单暴力,可时间复杂度却是O(n^3),对于有些***钻的多源最短路径问题,往往会超时。可我们不想用其他复杂的算法。那么,我们就可以对其进行优化。
第一层优化:利用矩阵的对称性
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=i;j++){
//只模拟下三角矩阵。
dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
dis[j][i]=dis[i][j];
}
}
}
}
第二层优化:只使用矩阵的下三角部分
由于矩阵是对称矩阵,所以我们只需要计算下三角的部分,最后根据我们要求的点来确定排序。即利用dis[i][j]=dis[j][i]
,若我们要求的两点i,j。则判断i和j的大小,再根据下三角的部分来解决即可。
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(k!=i){
//加入的中转顶点就是它自己的话没有任何意义,直接跳过。
int t=(k<i)?dis[i][k]:dis[k][i];//由于是对称的,这条语句其实是没有意义的。
//由于只取下三角部分,i对于我们来说是行号,k对应的就是加入顶点的列号,行号永远大于等于列号,若加入的顶点是大于行号的,由于是对称的,我们置换一下即可。
int temp=(k<i)?k:i;//得到最小的那个值,即为行号。
for(int j=1;j<=temp;j++){
//我们发现,这个式子中计算的就是第i行的顶点到第j列的顶点的最短距离,我们取了最小的那个。但我们最终还是要计算小于第i列的顶点。所以我们下面那个循环就完美的实现了哪一点。
dis[i][j]=min(dis[i][j],t+dis[k][j]);
}
//就是补全未计算的小于第i列的顶点。
for(int j=k+1;j<=i;j++){
dis[i][j]=min(dis[i][j],t+dis[j][k]);//注意要换成dis[j][k],因为我们在这个优化算法中只利用下三角矩阵(即只优化了下三角矩阵),故必须保证行大于等于列。
}
}
}
}
int start,end;//起点和终点,这里定义只是为了让你们知道
if(start>=end)cout<<dis[start][end]<<endl;
else cout<<dis[end][start]<<endl;
}
第三层优化:跳过不存在的边
由于有点边是不存在的,我们没必要往下进行计算,对于规模很大的图而边数却很少的话,这个优化非常关键
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(k!=i){
int t=(k<i)?dis[i][k]:dis[k][i];
if(t==inf)continue;//跳过不存在的边。
int temp=(k<i)?k:i;
for(int j=1;j<=temp;j++){
dis[i][j]=min(dis[i][j],t+dis[k][j]);
}
for(int j=k+1;j<=i;j++){
dis[i][j]=min(dis[i][j],t+dis[j][k]);
}
}
}
}
int start,end;//起点和终点,这里定义只是为了让你们知道
if(start>=end)cout<<dis[start][end]<<endl;
else cout<<dis[end][start]<<endl;
}
第四层优化:避免大量调用数学函数
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(k!=i){
int t=(k<i)?dis[i][k]:dis[k][i];
if(t==inf)continue;//跳过不存在的边。
int temp=(k<i)?k:i;
for(int j=1;j<=temp;j++){
if(t+dis[k][j]<dis[i][j])dis[i][j]=t+dis[k][j];
}
for(int j=k+1;j<=i;j++){
if(t+dis[k][j]<dis[i][j])dis[i][j]=t+dis[j][k];
}
}
}
}
int start,end;//起点和终点,这里定义只是为了让你们知道
if(start>=end)cout<<dis[start][end]<<endl;
else cout<<dis[end][start]<<endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!