非加权无向图—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;
}

猜你喜欢

转载自blog.csdn.net/hzf0701/article/details/107656987