如何修改Floyd-Warshall算法求解指定中间顶点数的Top-K最短路径?
修改Floyd-Warshall实现任意顶点对的Top-K最短路径(支持带环路径)
要实现你需求的「任意顶点对Top-K最短路径(允许路径含重复顶点),且自身到自身路径排除0代价空路径」,我们需要对Floyd-Warshall的状态定义和松弛逻辑做针对性修改,核心是从单条最短路径扩展为维护前K条最小代价路径的集合。
核心修改思路
原Floyd-Warshall用dist[i][j]存储i到j的单条最短路径代价,我们把它替换成二维列表数组topk[i][j]——每个列表专门存从i到j的前K条最短路径的代价,始终保持升序排列。同时,针对你提到的「v1到v1的路径不能是0」的要求,初始化时不给topk[i][i]设置0值,而是通过后续的环路径松弛生成有效代价。
具体实现步骤
1. 初始化阶段
首先准备图的邻接矩阵w,w[i][j]代表i到j的直接边代价(没有直接边就设为无穷大,比如float('inf'))。然后初始化topk数组:
- 对
i≠j的顶点对:如果w[i][j]不是无穷大,topk[i][j]初始化为[w[i][j]](直接边作为第一条候选路径); - 对
i=j的顶点对:topk[i][j]初始化为空列表(跳过0代价的空路径); - 其余无直接边的顶点对,
topk初始为空。
2. 扩展松弛逻辑
原Floyd-Warshall的松弛是取最小值,现在我们要组合所有可能的路径代价,筛选出前K个最小的:
按标准Floyd-Warshall的三层循环顺序(遍历所有中间顶点k,再遍历所有i、j),对每个(i,j):
- 生成候选代价:把
topk[i][k]里的每个代价,和topk[k][j]里的每个代价相加,得到所有i→...→k→...→j路径的总代价; - 合并筛选:把候选代价和
topk[i][j]原有的代价合并,去重后按升序排序,只保留前K个最小的代价更新topk[i][j]。
3. 适配自身到自身的非空路径
因为初始化时topk[i][i]是空的,后续通过松弛过程,比如i→k→i、i→k→l→i这类带环的路径代价会被逐步加入。你需要的「k=3个中间顶点(即4条边)的v1到v1路径代价6」,会在多次松弛后被筛选进topk[v1][v1]的前K项中。
结合你的例子验证
假设你的图权重如下:
w(v1,v3)=4,w(v3,v2)=1,w(v2,v3)=1,w(v3,v5)=2,w(v3,v1)=0(用于生成v1→v3→v2→v3→v1的6代价路径)- 其他无直接边的顶点对权重设为无穷大
关键松弛步骤
- 当中间顶点
k=v3时:- 生成
v1→v3→v1,代价4+0=4,加入topk[v1][v1]; - 生成
v3→v2→v3,代价1+1=2,加入topk[v3][v3];
- 生成
- 当中间顶点
k=v2时:- 生成
v1→v3→v2→v3,代价4+1+1=6,加入topk[v1][v3]; - 生成
v1→v3→v2→v3→v1,代价4+1+1+0=6,加入topk[v1][v1];
- 生成
- 当中间顶点
k=v3再次遍历:- 生成
v1→v3→v2→v3→v5,代价4+1+1+2=8,加入topk[v1][v5];
- 生成
最终结果
- 当K=3时,
topk[v1][v1]的前3条路径代价包含6(假设前两条是4和6,第三条是更高的代价); topk[v1][v5]的路径代价中,8会被纳入前K项,符合你给出的例子要求。
注意点
- 带环路径允许:如果需要限制路径为无重复顶点的简单路径,得额外记录路径中的顶点集合,复杂度会大幅上升——你的例子里包含重复的v3,所以我们采用允许带环的逻辑;
- 去重与排序:每次合并后必须去重排序,否则会出现重复代价,且无法保证前K个是最小的;
- 无穷大值:初始化时用足够大的数值表示无直接边,避免无效的路径组合。
内容的提问来源于stack exchange,提问作者Subhamm
相关产品推荐
相关产品推荐

