引入
狄傑斯特拉(Dijstra)算法解決的問題是:從圖G中的某個指定頂點vk開始到其余各個頂點的最短路徑,其中圖G有n個頂點,k∈[0, n-1]。若還需要求某個頂點vx開始到其余各個頂點的最短路徑(其中x≠k),則還需要再跑一次Dijstra算法。若用戶需要圖G中每個頂點到其余頂點最短路徑呢?則需要跑n次Dijstra算法。由於Dijstra算法的時間復雜度為O(n2),所以跑n次,則時間復雜度為O(n3)。有沒有一種算法能夠只跑一次就獲得所有頂點到其余頂點的最短路徑呢?弗洛伊德(Floyd)算法!
弗洛伊德算法解決的問題是:具有n個頂點的圖G中的所有頂點到其它所有頂點的最短路徑。即一次性求得頂點vk到頂點集V-vk中各頂點的最短路徑。例如:一次性求得,頂點v0到v0、v1、v2、v3、……、vn-1;v1到各頂點;v2到各頂點;………;vn-1到各頂點的最短路徑。
弗洛伊德算法適用於需要求所有頂點至所有頂點的最短路徑問題。並且既適用於無向圖也適用於有向圖。
核心思想
-
對於圖G中相異的任意兩個頂點vi和vj,其中i≠j,vi要么直接與vj相連,要么經過圖G中的某個頂點vk與vj相連。若D(i, j) > D(i, k) + D(k, j),則vi經過vk到達vj比直接從vi到達vj的距離(或權值或隨便你像賦予的任何意義)更短,那么記錄下此時的D(i, j)。並用P(i, j)記錄下此時的k,即vi到vk,再從vk到vj(vi和vk之間可能還有別的頂點,同理vk和vj之間也可能還有別的頂點。)(用D(i, j)表示頂點vi到頂點vj的距離。可直達/可中轉。)
-
對於圖G中相同的任意兩個頂點vi和vj,其中i=j,自己到自己的距離一定是0,所以D(i, i)=D(j, j)=0,且P(i, i)=P(j, j)=i或j。舉例:若v2到v2,則i=2,j=2;且不會經過任意別的頂點,只經過自己,即v2。
問題:下圖中各頂點到其余各個頂點的最短路徑是什么?
推演
用Path[i][j].Length表示vi->vj的路徑長度;用Path[i][j].Via表示vi->vj經過頂點Via。(vi->Via之間可以有其它頂點;Via->vj
之間也可由其它頂點。)
用鄰接矩陣graph表示圖G。graph[i][j]表示vi->vj的權值(距離或任何你想賦予的含義。)
初始狀態Path[i][j].Length==∞即vi->vj無路徑(對於邊來說∞表示無邊)。
將頂點v0、v1、……、vn-1共n個頂點逐個加入到各條路徑Path中,測試是否滿足vi->vk->vj(即從頂點vi經過vk到達vj能否構成一條路徑)且若Path[i][j].Length > Path[i][k].Length + Path[k][j].Length則Path[i][j].Length = Path[i][k].Length + Path[k][j].Length;Path[i][j].Via = k(輸出結果時要用到遞歸。)或Path[i][j].Via=Path[i][k].Via(輸出結果時代碼簡單。)
具體的動畫演示步驟可參看大話數據結構里的PPT。鏈接:《大話數據結構 溢彩加強版》相關主題 - 伍迷 - 博客園 (cnblogs.com)
書中問題
若使用編程語言(計算機)能表示的最大整數表示無窮,則在比較路徑的長短(權值的大小或你賦予的任何意義)時,可能會產生計算溢出。雖然書上(包括最新版的溢彩版(2020年12月第1印次))的代碼使用65535來表示無窮(即216-1,16位無符號整型值的最大值)在32位整型值的系統上並無所謂,但是若權值大於65535呢?又或許原本作者就是想用最大整型值表示無窮?
我的疑問
這樣寫代碼,最后輸出的Paths數組確實更便於整理輸出vi->vj的路徑。可是P[v][w] = P[v][k]具體含義是什么?我不覺得是前驅。vi->vk之間可以有更多的頂點,vk->vj之間也可以有更多的頂點。
代碼
用鄰接矩陣來表示圖G。如下:
C#代碼
using System;
using System.Collections.Generic;
namespace Floyd
{
class Program
{
static void Main(string[] args)
{
const int inf = Constants.Infinity; // 另設常量表示無窮
const int numberOfVertexes = 9;
int[][] graph = new int[][] {
new int[]{0, 1, 5, inf, inf, inf, inf, inf, inf },
new int[]{ 1, 0, 3, 7, 5, inf, inf, inf, inf },
new int[]{ 5, 3, 0, inf, 1, 7, inf, inf, inf },
new int[]{ inf, 7, inf, 0, 2, inf, 3, inf, inf },
new int[]{ inf, 5, 1, 2, 0, 3, 6, 9, inf },
new int[]{ inf, inf, 7, inf, 3, 0, inf, 5, inf },
new int[]{ inf, inf, inf, 3, 6, inf, 0, 2, 7 },
new int[]{ inf, inf, inf, inf, 9, 5, 2, 0, 4 },
new int[]{ inf, inf, inf, inf, inf, inf, 7, 4, 0 },
};
Path[][] paths = Floyd(graph, numberOfVertexes);
Print(paths, numberOfVertexes);
}
/// <summary>
/// 使用Floyd算法計算含有numberOfVertexes個節點的圖graph的各點到其余各點的最
/// 短路徑。
/// </summary>
///
/// <param name="graph">以鄰接矩陣表示的圖graph。行下標為起點頂點的i,列下標
/// 為終點頂點。</param>
///
/// <param name="numberOfVertexes">圖graph中的頂點個數。</param>
///
/// <returns>Path二維數組類。每個Path元素含有以行為下標作為起點頂點到以列為下
/// 標作為終點頂點的最短路徑和途徑頂點。</returns>
static Path[][] Floyd(int[][] graph, int numberOfVertexes)
{
Path[][] paths = new Path[numberOfVertexes][]; // 創建數組的第一維
for (int i = 0; i < numberOfVertexes; i++)
{
paths[i] = new Path[numberOfVertexes]; // 創建數組的第二維
for (int j = 0; j < numberOfVertexes; j++)
{
paths[i][j] = new Path
{
Length = Constants.Infinity, // 無窮表示無路徑
Via = -1 // 不經過任何頂點
};
}
}
for (int i = 0; i < numberOfVertexes; i++) // 初始化
{
for (int j = 0; j < numberOfVertexes; j++)
{
if (graph[i][j] != Constants.Infinity)
{
paths[i][j].Length = graph[i][j]; // 圖中現有各點的連
// 通情況
paths[i][j].Via = j; // 設終點即途經點
}
}
}
/**
* 測試Vi->Vk->Vj是否有連通的路徑且路徑更短。
*/
for (int k = 0; k < numberOfVertexes; k++) // 以Vk作為途經點。
{
for (int i = 0; i < numberOfVertexes; i++) // 以Vi作為起點。
{
for (int j = 0; j < numberOfVertexes; j++) // 以Vj作為終點。
{
if ((paths[i][k].Length != Constants.Infinity && // Vi->Vk有路徑且
paths[k][j].Length != Constants.Infinity) && // Vk->Vj有路徑
// 途經Vk的路徑長度小於已知的Vi->Vj的路徑。
(paths[i][k].Length + paths[k][j].Length < paths[i][j].Length))
{
paths[i][j].Length = paths[i][k].Length + paths[k][j].Length;
paths[i][j].Via = k;
/**
* 經過前面的這個路徑到達現在的位置。書上用的這種方式。輸
* 出路徑的方式不同。
*/
//paths[i][j].Via = paths[i][k].Via;
}
//if ((paths[i][k].Length + paths[k][j].Length < paths[i][j].Length))
//{ // 重現加法溢出問題。
// paths[i][j].Length = paths[i][k].Length + paths[k][j].Length;
// paths[i][j].Via = k;
// /**
// * 經過前面的這個路徑到達現在的位置。書上用的這種方式。輸
// * 出路徑的方式不同。
// */
// //paths[i][j].Via = paths[i][k].Via;
//}
}
}
}
/*
* 循環結束,找到圖graph中各頂點到其余各頂點的最短距離(Length記錄)及路徑
* (Via記錄)。
*/
return paths;
}
/// <summary>
/// 輸出圖中的各條路徑。
/// </summary>
/// <param name="paths">已計算獲得的路徑。</param>
/// <param name="numberOfVertexes">圖的頂點數目。</param>
static void Print(Path[][] paths, int numberOfVertexes)
{
for (int i = 0; i < numberOfVertexes; i++)
{
Console.WriteLine($"源點V{i}到其余各點的路徑及距離:");
for (int j = 0; j < numberOfVertexes; j++)
{
//Console.WriteLine($"V{i}到V{j}:{paths[i][j].Length}");
//Console.WriteLine(PrintPath1Recursion(paths, i, j, numberOfVertexes));
Console.WriteLine(PrintPath1(paths, i, j, numberOfVertexes));
/**
* 《大話數據結構》的方式輸出。
*/
//Console.WriteLine(PrintPath2(paths, i, j, numberOfVertexes));
}
Console.WriteLine();
}
}
/// <summary>
/// 輸出Vi->Vj的路徑及距離。(按自己思路的路徑表示形式)
/// </summary>
/// <param name="paths">已計算獲得的各路徑的最短路徑。</param>
/// <param name="start">起始頂點(下標)。</param>
/// <param name="end">終點頂點(下標)。</param>
/// <param name="numberOfVertexes">圖G的頂點數目。</param>
/// <returns>描述路徑及距離的字符串。如:0->1->2->4->3->6: 10</returns>
static string PrintPath1Recursion(Path[][] paths, int start, int end, int numberOfVertexes)
{
int i = start,
j = end;
int len = paths[i][j].Length;
string result = "";
if (paths[i][j].Via != j)
{
int k = paths[i][j].Via;
result = $"{PrintPath1Recursion(paths, i, k, numberOfVertexes)}";
result = $"{result}\n{PrintPath1Recursion(paths, k, j, numberOfVertexes)}";
}
else
{
result = $"{i}->{j}: {len}";
}
return result;
}
/// <summary>
/// 輸出Vi->Vj的路徑及距離。(按自己思路的路徑表示形式)
/// </summary>
/// <param name="paths">已計算獲得的各路徑的最短路徑。</param>
/// <param name="start">起始頂點(下標)。</param>
/// <param name="end">終點頂點(下標)。</param>
/// <param name="numberOfVertexes">圖G的頂點數目。</param>
/// <returns>描述路徑及距離的字符串。如:0->1->2->4->3->6: 10</returns>
static string PrintPath1(Path[][] paths, int start, int end, int numberOfVertexes)
{
int i = start,
j = end,
len = paths[start][end].Length,
k = paths[i][j].Via;
string result = "";
Stack<int> s = new Stack<int>();
s.Push(j);
while (s.Count != 0)
{
j = s.Pop();
if (paths[i][j].Via != j)
{
s.Push(j);
k = paths[i][j].Via;
s.Push(k);
}
else
{
result = string.IsNullOrEmpty(result) ? $"{i}" : $"{result}->{i}";
i = j;
}
}
result = $"{result}->{j}: {len}";
return result;
}
/// <summary>
/// 輸出Vi->Vj的路徑及距離。(按《大話數據結構》上的路徑表示形式)
/// </summary>
/// <param name="paths">已計算獲得的各路徑的最短路徑。</param>
/// <param name="start">起始頂點(下標)。</param>
/// <param name="end">終點頂點(下標)。</param>
/// <param name="numberOfVertexes">圖G的頂點數目。</param>
/// <returns>描述路徑及距離的字符串。如:0->1->2->4->3->6: 10</returns>
static string PrintPath2(Path[][] paths, int start, int end, int numberOfVertexes)
{
// 自己到自己和自己到別的頂點的情況。
int i = start, // Vi
j = end; // Vj
string result = i == j ? $"{i}->{j}" : $"{i}";
int len = paths[i][j].Length;
while (i != j)
{
i = paths[i][j].Via;
result = $"{result}->{i}";
}
result = $"{result}: {len}";
return result;
}
}
/// <summary>
/// 表示常量的類。
/// </summary>
public static class Constants
{
/// <summary>
/// 用整數的最大值表示無窮∞。
/// </summary>
public const int Infinity = int.MaxValue;
}
/// <summary>
/// 表示路徑的類。
/// Length:點Vi->Vj的路徑長度。
/// Via:頂點Vi->Via->Vj,即頂點Vi經過頂點Via到達頂點Vj。
/// Vi->Via之間可能還會有其它的頂點;Via->Vj之間可能還會有其它的頂點。
/// 輸出路徑時不能直接輸出Vi->Via->Vj,要找到他們之間其它點。
/// </summary>
public class Path
{
/// <summary>
/// 頂點Vi->Vj的路徑長度。至於i和j為多少?
/// 可以用Path類二維數組,行表示i,列表示j。
/// </summary>
public int Length { get; set; } = Constants.Infinity;
/// <summary>
/// 頂點Vi->Via->Vj,即頂點Vi經過頂點Via到達頂點Vj。
/// </summary>
public int Via { get; set; } = -1;
}
}
TypeScript代碼
/**
* 用整數的最大值表示無窮∞。
*/
const infinity: number = Number.MAX_VALUE;
/**
* 表示路徑的類。
* Length:點Vi->Vj的路徑長度。
* Via:頂點Vi->Via->Vj,即頂點Vi經過頂點Via到達頂點Vj。Vi->Via之間可能還會有其它頂點;Via->Vj之間可能還會有其它的頂點。
* 輸出路徑時不能直接輸出Vi->Via->Vj,要找到他們之間其它點。
*/
class Path {
/**
* 頂點Vi->Vj的路徑長度。至於i和j為多少?可以用Path類二維數組,行表示i,列表示j。
*/
Length: number = infinity;
/**
* 頂點Vi->Via->Vj,即頂點Vi經過頂點Via到達頂點Vj。
*/
Via: number = -1;
}
/**
* 使用Floyd算法計算含有numberOfVertexes個節點的圖graph的各點到其余各點的最短路徑。
* @param graph 以鄰接矩陣表示的圖graph。行下標為起點頂點的i,列下標
* @param numberOfVertexes 圖graph中的頂點個數。
* @returns Path二維數組類。每個Path元素含有以行為下標作為起點頂點到以列為下標作為終點頂點的最短路徑和途徑頂點。
* @author kokiafan
*/
function Floyd(graph: number[][], numberOfVertexes: number): Path[][] {
let paths: Path[][] = []; // 創建數組的第一維
for (let i = 0; i < numberOfVertexes; i++) {
paths[i] = []; // 創建數組的第二維
for (let j = 0; j < numberOfVertexes; j++) {
paths[i][j] = new Path();
paths[i][j].Length = infinity; // 無窮表示無路徑
paths[i][j].Via = -1; // 不經過任何頂點
}
}
for (let i = 0; i < numberOfVertexes; i++) // 初始化
{
for (let j = 0; j < numberOfVertexes; j++) {
if (graph[i][j] != infinity) {
paths[i][j].Length = graph[i][j]; // 圖中現有各點的連通情況
paths[i][j].Via = j; // 設終點即途經點
}
}
}
/**
* 測試Vi->Vk->Vj是否有連通的路徑且路徑更短。
*/
for (let k = 0; k < numberOfVertexes; k++) // 以Vk作為途經點。
{
for (let i = 0; i < numberOfVertexes; i++) // 以Vi作為起點。
{
for (let j = 0; j < numberOfVertexes; j++) // 以Vj作為終點。
{
if ((paths[i][k].Length != infinity && // Vi->Vk有路徑且
paths[k][j].Length != infinity) && // Vk->Vj有路徑
// 途經Vk的路徑長度小於已知的Vi->Vj的路徑。
(paths[i][k].Length + paths[k][j].Length < paths[i][j].Length)) {
paths[i][j].Length = paths[i][k].Length + paths[k][j].Length;
paths[i][j].Via = k;
/**
* 經過前面的這個路徑到達現在的位置。書上用的這種方式。輸
* 出路徑的方式不同。
*/
//paths[i][j].Via = paths[i][k].Via;
}
// if ((paths[i][k].Length + paths[k][j].Length < paths[i][j].Length)) {
// // 重現加法溢出問題。
// paths[i][j].Length = paths[i][k].Length + paths[k][j].Length;
// paths[i][j].Via = k;
// /**
// * 經過前面的這個路徑到達現在的位置。書上用的這種方式。輸
// * 出路徑的方式不同。
// */
// //paths[i][j].Via = paths[i][k].Via;
// }
}
}
}
/*
* 循環結束,找到圖graph中各頂點到其余各頂點的最短距離(Length記錄)及路徑(Via記錄)。
*/
return paths;
}
/**
* 輸出Vi->Vj的路徑及距離。(按自己思路的路徑表示形式)
* @param paths 已計算獲得的各路徑的最短路徑。
* @param start 起始頂點(下標)。
* @param end 終點頂點(下標)。
* @param numberOfVertexes 圖G的頂點數目。
* @returns 描述路徑及距離的字符串。如:0->1->2->4->3->6: 10
* @author kokiafan
*/
function PrintPath1Recursion(paths: Path[][], start: number, end: number, numberOfVertexes: number): string {
let i: number = start,
j: number = end,
len: number = paths[i][j].Length;
let result: string = "";
if (paths[i][j].Via != j) {
let k: number = paths[i][j].Via;
result = `${PrintPath1Recursion(paths, i, k, numberOfVertexes)}`;
result = `${result}\n${PrintPath1Recursion(paths, k, j, numberOfVertexes)}`;
}
else {
result = `${i}->${j}: ${len}`;
}
return result;
}
/**
* 輸出Vi->Vj的路徑及距離。(按自己思路的路徑表示形式)
* @param paths 已計算獲得的各路徑的最短路徑。
* @param start 起始頂點(下標)。
* @param end 終點頂點(下標)。
* @param numberOfVertexes 圖G的頂點數目。
* @returns 描述路徑及距離的字符串。如:0->1->2->4->3->6: 10
* @author kokiafan
*/
function PrintPath1(paths: Path[][], start: number, end: number, numberOfVertexes: number): string {
let i: number = start,
j = end,
len = paths[start][end].Length,
k = paths[i][j].Via;
let result: string = "";
let s: number[] = [];
s.push(j);
while (s.length != 0) {
j = s.pop();
if (paths[i][j].Via != j) {
s.push(j);
k = paths[i][j].Via;
s.push(k);
}
else {
if (!result) {
result = `${i}`;
}
else {
result = `${result}->${i}`;
}
i = j;
}
}
result = `${result}->${j}: ${len}`;
return result;
}
/**
* 輸出Vi->Vj的路徑及距離。(按《大話數據結構》上的路徑表示形式)
* @param paths 已計算獲得的各路徑的最短路徑。
* @param start 起始頂點(下標)。
* @param end 終點頂點(下標)。
* @param numberOfVertexes 圖G的頂點數目。
* @returns 描述路徑及距離的字符串。如:0->1->2->4->3->6: 10
* @author kokiafan
*/
function PrintPath2(paths: Path[][], start: number, end: number, numberOfVertexes: number): string {
// 自己到自己和自己到別的頂點的情況。
let i: number = start, // Vi
j = end; // Vj
let result: string = i == j ? `${i}->${j}` : `${i}`;
let len: number = paths[i][j].Length;
while (i != j) {
i = paths[i][j].Via;
result = `${result}->${i}`;
}
result = `${result}: ${len}`;
return result;
}
/**
* 輸出圖中的各條路徑。
* @param paths 已計算獲得的路徑。
* @param numberOfVertexes 圖的頂點數目。
* @author kokiafan
*/
function Print(paths: Path[][], numberOfVertexes: number): void {
for (let i = 0; i < numberOfVertexes; i++) {
console.log(`源點V${i}到其余各點的路徑及距離:`);
for (let j = 0; j < numberOfVertexes; j++) {
//console.log(`V${i}到V${j}:${paths[i][j].Length}\n${PrintPath1Recursion(paths, i, j, numberOfVertexes)}`);
console.log(PrintPath1(paths, i, j, numberOfVertexes));
/**
* 《大話數據結構》的方式輸出。
*/
//console.log(PrintPath2(paths, i, j, numberOfVertexes));
}
console.log();
}
}
function Main(): void {
const inf: number = infinity; // 另設常量表示無窮
const numberOfVertexes: number = 9;
let graph: number[][] = [
[0, 1, 5, inf, inf, inf, inf, inf, inf],
[1, 0, 3, 7, 5, inf, inf, inf, inf],
[5, 3, 0, inf, 1, 7, inf, inf, inf],
[inf, 7, inf, 0, 2, inf, 3, inf, inf],
[inf, 5, 1, 2, 0, 3, 6, 9, inf],
[inf, inf, 7, inf, 3, 0, inf, 5, inf],
[inf, inf, inf, 3, 6, inf, 0, 2, 7],
[inf, inf, inf, inf, 9, 5, 2, 0, 4],
[inf, inf, inf, inf, inf, inf, 7, 4, 0],
];
let paths: Path[][] = Floyd(graph, numberOfVertexes);
Print(paths, numberOfVertexes);
console.log(paths);
}
Main();
/**
PrintPath1方法的輸出:
源點V0到其余各點的路徑及距離:
0->0: 0
0->1: 1
0->1->2: 4
0->1->2->4->3: 7
0->1->2->4: 5
0->1->2->4->5: 8
0->1->2->4->3->6: 10
0->1->2->4->3->6->7: 12
0->1->2->4->3->6->7->8: 16
源點V1到其余各點的路徑及距離:
1->0: 1
1->1: 0
1->2: 3
1->2->4->3: 6
1->2->4: 4
1->2->4->5: 7
1->2->4->3->6: 9
1->2->4->3->6->7: 11
1->2->4->3->6->7->8: 15
源點V2到其余各點的路徑及距離:
2->1->0: 4
2->1: 3
2->2: 0
2->4->3: 3
2->4: 1
2->4->5: 4
2->4->3->6: 6
2->4->3->6->7: 8
2->4->3->6->7->8: 12
源點V3到其余各點的路徑及距離:
3->4->2->1->0: 7
3->4->2->1: 6
3->4->2: 3
3->3: 0
3->4: 2
3->4->5: 5
3->6: 3
3->6->7: 5
3->6->7->8: 9
源點V4到其余各點的路徑及距離:
4->2->1->0: 5
4->2->1: 4
4->2: 1
4->3: 2
4->4: 0
4->5: 3
4->3->6: 5
4->3->6->7: 7
4->3->6->7->8: 11
源點V5到其余各點的路徑及距離:
5->4->2->1->0: 8
5->4->2->1: 7
5->4->2: 4
5->4->3: 5
5->4: 3
5->5: 0
5->7->6: 7
5->7: 5
5->7->8: 9
源點V6到其余各點的路徑及距離:
6->3->4->2->1->0: 10
6->3->4->2->1: 9
6->3->4->2: 6
6->3: 3
6->3->4: 5
6->7->5: 7
6->6: 0
6->7: 2
6->7->8: 6
源點V7到其余各點的路徑及距離:
7->6->3->4->2->1->0: 12
7->6->3->4->2->1: 11
7->6->3->4->2: 8
7->6->3: 5
7->6->3->4: 7
7->5: 5
7->6: 2
7->7: 0
7->8: 4
源點V8到其余各點的路徑及距離:
8->7->6->3->4->2->1->0: 16
8->7->6->3->4->2->1: 15
8->7->6->3->4->2: 12
8->7->6->3: 9
8->7->6->3->4: 11
8->7->5: 9
8->7->6: 6
8->7: 4
8->8: 0
----------------------------------------
PrintPath2方法的輸出:
源點V0到其余各點的路徑及距離:
0->0: 0
0->1: 1
0->1->2: 4
0->1->2->4->3: 7
0->1->2->4: 5
0->1->2->4->5: 8
0->1->2->4->3->6: 10
0->1->2->4->3->6->7: 12
0->1->2->4->3->6->7->8: 16
源點V1到其余各點的路徑及距離:
1->0: 1
1->1: 0
1->2: 3
1->2->4->3: 6
1->2->4: 4
1->2->4->5: 7
1->2->4->3->6: 9
1->2->4->3->6->7: 11
1->2->4->3->6->7->8: 15
源點V2到其余各點的路徑及距離:
2->1->0: 4
2->1: 3
2->2: 0
2->4->3: 3
2->4: 1
2->4->5: 4
2->4->3->6: 6
2->4->3->6->7: 8
2->4->3->6->7->8: 12
源點V3到其余各點的路徑及距離:
3->4->2->1->0: 7
3->4->2->1: 6
3->4->2: 3
3->3: 0
3->4: 2
3->4->5: 5
3->6: 3
3->6->7: 5
3->6->7->8: 9
源點V4到其余各點的路徑及距離:
4->2->1->0: 5
4->2->1: 4
4->2: 1
4->3: 2
4->4: 0
4->5: 3
4->3->6: 5
4->3->6->7: 7
4->3->6->7->8: 11
源點V5到其余各點的路徑及距離:
5->4->2->1->0: 8
5->4->2->1: 7
5->4->2: 4
5->4->3: 5
5->4: 3
5->5: 0
5->7->6: 7
5->7: 5
5->7->8: 9
源點V6到其余各點的路徑及距離:
6->3->4->2->1->0: 10
6->3->4->2->1: 9
6->3->4->2: 6
6->3: 3
6->3->4: 5
6->7->5: 7
6->6: 0
6->7: 2
6->7->8: 6
源點V7到其余各點的路徑及距離:
7->6->3->4->2->1->0: 12
7->6->3->4->2->1: 11
7->6->3->4->2: 8
7->6->3: 5
7->6->3->4: 7
7->5: 5
7->6: 2
7->7: 0
7->8: 4
源點V8到其余各點的路徑及距離:
8->7->6->3->4->2->1->0: 16
8->7->6->3->4->2->1: 15
8->7->6->3->4->2: 12
8->7->6->3: 9
8->7->6->3->4: 11
8->7->5: 9
8->7->6: 6
8->7: 4
8->8: 0
*/
參考資料
《大話數據結構》 - 程傑 著 - 清華大學出版社 第268頁