引入

从A点到B点的最短路径是什么?求最短路径的两种算法:Dijkstra算法和Floyd算法。

网图:带权图。

非网图最短路径:两顶点间经过的边数最少的路径。(非网图也可被理解为各边权值为1的网图。)

网图最短路径:两顶点间经过的边上权值之和最少的路径。路径上第一个顶点是源点,最后的顶点是终点。

问题:下图中V0 点到其余各个顶点Vk的最短路径是什么?

演示

设图G中的每个顶点为V0到该点的路径。并用以下形式来表示:

Path[x].Length:V0到该路径所处终点的V[x]的最短路径。如Path[2].Length为V0->V2的最短路径。

Path[x].Predecessor:V0到该路径所处终点的上一个顶点的编号(下标)。如Path[2].Predecessor为0,表示V0->V2的最短路径,顶点V2的前一个顶点为V1,即经过V1然后到达V2

Path[x].IsVisited:顶点Vx是否曾作为立足点来查找接下来的最短路径。其中Vx是V0到该路径所处的终点。

G[i][j]:用邻接矩阵表示图G。G[i][j]为顶点Vi到顶点Vj的权值。

0.初始化:

所有路径的Predecessor设为-1,Length设为0,IsVisited设为false。

从源点开始Path[0].Predecessor = 0,因为V0->V0路径上的一个顶点为V0,V0->V0的距离为0,故Path[0].Length = 0。

步骤0:

1.以V0为立足点,所以Path[0].IsVisited = true。

2.V0与V0、V1及V2相连。

Path[0].IsVisited为true,故跳过这个路径。

Path[0].Length + G[0][1] = 0 + 1 < Path[2].Length = ∞故Path[1].Length = 1,Path[1].Predecessor = 0;

Path[0].Length + G[0][2] = 0 + 5 < Path[2].Length = ∞故Path[2].Length = 5,Path[2].Predecessor = 0;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0].IsVisited为true,故跳过。而其余的为false。

Path[1].Length为1,Path[2].Length为5,其余Path.Length为∞。故选Path[1]的终点V1作为新的立足点。令V0 = 1,从顶点V1开始下一轮寻找。

步骤1:

1.以V1为立足点,所以Path[1].IsVisited = true。

2.V1与V0、V1、V2、V3及V4相连。

Path[0].IsVisited为true,Path[1].IsVisited为true,故跳过这些路径。

Path[1].Length + G[1][2] = 1 + 3 < Path[2].Length = 5故Path[2].Length = 4,Path[2].Predecessor = 1;

Path[1].Length + G[1][3] = 1 + 7 < Path[3].Length = ∞故Path[3].Length = 8,Path[3].Predecessor = 1;

Path[1].Length + G[1][4] = 1 + 5 < Path[4].Length = ∞故Path[4].Length = 6,Path[4].Predecessor = 1;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1].IsVisited为true,故跳过。而其余的为false。

Path[2].Length为4,Path[3].Length为8,Path[4].Length为6,其余Path.Length为∞。故选Path[2]的终点V2作为新的立足点。令V0 = 2,从顶点V2开始下一轮寻找。

步骤2:

1.以V2为立足点,所以Path[2].IsVisited = true。

2.V2与V0、V1、V2、V4、及V5相连。

Path[0、1、2].IsVisited为true,故跳过这些路径。

Path[2].Length + G[2][4] = 4 + 1 < Path[4].Length = 6故Path[4].Length = 5,Path[4].Predecessor = 2;

Path[2].Length + G[2][5] = 4 + 7 < Path[5].Length = ∞故Path[5].Length = 11,Path[5].Predecessor = 2;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2].IsVisited为true,故跳过。而其余的为false。

Path[3].Length为8,Path[4].Length为5,Path[5].Length为11,其余Path.Length为∞。故选Path[4]的终点V4作为新的立足点。令V0 = 4,从顶点V4开始下一轮寻找。

步骤3:

1.以V4为立足点,所以Path[4].IsVisited = true。

2.V4与V1、V2、V3、V4、V5、V6及V7相连。

Path[0、1、2、4].IsVisited为true,故跳过这些路径。

Path[4].Length + G[4][3] = 5 + 2 < Path[3].Length = 8故Path[3].Length = 7,Path[3].Predecessor = 4;

Path[4].Length + G[4][5] = 5 + 3 < Path[5].Length = 11故Path[5].Length = 8,Path[5].Predecessor = 4;

Path[4].Length + G[4][6] = 5 + 6 < Path[6].Length = ∞故Path[6].Length = 11,Path[6].Predecessor = 4;

Path[4].Length + G[4][7] = 5 + 9 < Path[7].Length = ∞故Path[7].Length = 14,Path[6].Predecessor = 4;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、4].IsVisited为true,故跳过。而其余的为false。

Path[3].Length为7,Path[5].Length为8,Path[6].Length为11,Path[7].Length为14,其余Path.Length为∞。故选Path[3]的终点V3作为新的立足点。令V0=3,从顶点V3开始下一轮寻找。

步骤4:

1.以V3为立足点,所以Path[3].IsVisited = true。

2.V3与V1、V4及V6相连。

Path[0、1、2、3、4].IsVisited为true,故跳过这些路径。

Path[3].Length + G[3][6] = 7 + 3 < Path[6].Length = 11故Path[6].Length = 10,Path[6].Predecessor = 3;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、3、4].IsVisited为true,故跳过。而其余的为false。

Path[5].Length为8,Path[6].Length为10,Path[7].Length为14,其余Path.Length为∞。故选Path[5]的终点V5作为新的立足点。令V0 = 5,从顶点V5开始下一轮寻找。

步骤5:

1.以V5为立足点,所以Path[5].IsVisited = true。

2.V5与V2、V4、V5及V7相连。

Path[0、1、2、3、4、5].IsVisited为true,故跳过这些路径。

Path[5].Length + G[5][7] = 8 + 5 < Path[7].Length = 14故Path[7].Length = 13,Path[7].Predecessor = 5;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、3、4、5].IsVisited为true,故跳过。而其余的为false。

Path[6].Length为10,Path[7].Length为13,其余Path.Length为∞。故选Path[6]的终点V6作为新的立足点。令V0 = 6,从顶点V6开始下一轮寻找。

步骤6:

1.以V6为立足点,所以Path[6].IsVisited = true。

2.V6与V3、V4、V7及V8相连。

Path[0、1、2、3、4、5、6].IsVisited为true,故跳过这些路径。

Path[6].Length + G[6][7] = 10 + 2 < Path[7].Length = 13故Path[7].Length = 12,Path[7].Predecessor = 6;

Path[6].Length + G[6][8] = 10 + 7 < Path[8].Length = ∞故Path[8].Length = 17,Path[8].Predecessor = 6;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、3、4、5、6].IsVisited为true,故跳过。而其余的为false。

Path[7].Length为12,Path[8].Length为17,没有其余Path了。故选Path[7]的终点V7作为新的立足点。令V0 = 7,从顶点V7开始下一轮寻找。

步骤7:

1.以V7为立足点,所以Path[7].IsVisited = true。

2.V7与V4、V5、V6、V7及V8相连。

Path[0、1、2、3、4、5、6、7].IsVisited为true,故跳过这些路径。

Path[7].Length + G[7][8] = 12 + 4<Path[8].Length = 17故Path[8].Length = 16,Path[8].Predecessor = 7;

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、3、4、5、6、7].IsVisited为true,故跳过。而其余的为false。

Path[8].Length为16,无其余Path。故选Path[8]的终点V8作为新的立足点。令V0 = 8,从顶点V8开始下一轮寻找。

步骤8:

1.以V8为立足点,所以Path[8].IsVisited = true。

2.V8与V6、V7及V8相连。

Path[0、1、2、3、4、5、6、7、8].IsVisited为true,故跳过这些路径。

已无没有探索过的路径了。

3.现查找图G中所有9个路径(每个顶点皆构成一个最短路径)中未曾作为立足点且路径最短的哪个路径的终点作为新的立足点。

Path[0、1、2、3、4、5、6、7、8].IsVisited为true,故跳过。已无没有探索过的路径了。无需开始下一轮的寻找。

完成探索,Path数组即是V0到各顶点的最短路径。输出Path数组即可。

步骤0~8中的操作都是重复的,总结形成代码。

伪代码

Dijkstra(Graph g, int v, int n)
{
// 0. 初始化。
Path[] paths = new Path[n]; // 将每个路径设为初始值。
for (int i = 0; i < n; i++)
{
paths[i].Length = ∞;
paths[i].Predecessor = -1;
paths[i].IsVisited = false;
} // 以v为源点寻找最短路径。
int k = v;
// v0->v0的路径长度为0。
paths[k].Length = 0;
// v0->v0路径的上一个顶点为v0。
paths[k].Predecessor = 0; // 逐个顶点探索最短路径。
for (int i = 0; i < n; i++)
{
// 1.以vk为立足点寻找它到其余顶点的最短路径。
paths[k].IsVisited = true;
// 2.探索vk的最短路径
for (int j = 0; j < n; j++)
{
// vj未曾作为立足点 &&
// 存在边(vk, vj) &&
// vk到vj的当前路径长度比已经探索到的源点到vj的路径还更短。
if (paths[j].IsVisited = false &&
g[k][j] != ∞ &&
paths[k].Length + g[k][j] < paths[j].Length)
{
// 更新源点到vj的路径(paths[k]是源点到vk的最短路径)。
paths[j].Length = paths[k].Length + g[k][j];
// 路径j的上一个顶点应该更新为k(即源点到vj是经过vk到达vj的)。
paths[j].Predecessor = k;
}
} // 3.寻找图G中已知的最短路径。并以该路径的终点为新的立足点探索最短路径。
// 设当前最小值为无穷。
int min = ∞;
// 遍历所有路径。
for (int j = 0; j < n; j++)
{
// 路径的终点曾作为立足点的路径,其已是最短路径。
// 该路径的终点无需再作为立足点去探索最短路径。
// 故,直接跳过。
if (paths[j].IsVisited == true)
{
continue;
} if (paths[j].Length < min)
{
min = paths[j].Length;
k = j;
}
} // 此时k即是新的最短路径的下标。
} // 输出paths,每条路径皆为源点到该路径的终点的最短路径。
}

分析

Dijkstra算法解决了从某源点到其余各点的最短路径问题。从循环嵌套可知算法的时间复杂度为O(n2)。(摘自《大话数据结构》。)

最小生成树与最小路径的区别

最小生成树:将图G中所有顶点相连所用的路程最短(所有路径之和最小)。保证图中的所有路径之和最短。但某个点到另一个点是否最近,不能保证。

最小路径:从图G某各顶点出发,到其它顶点所用路程最短。保证某个点到其余点路程最短,但把所有点连接起来是否路程最短,就不一定了。

例如:下面这幅图的最小生成树和最小路径。

代码

用邻接矩阵来表示图G。如下:

C#代码

using System;

namespace Dijkstra
{
class Program
{
static void Main(string[] args)
{
int numberOfVertexes = 9,
infinity = Constants.Infinity; int[][] graph = new int[][] {
new int[]{0, 1, 5, infinity, infinity, infinity, infinity, infinity, infinity },
new int[]{ 1, 0, 3, 7, 5, infinity, infinity, infinity, infinity },
new int[]{ 5, 3, 0, infinity, 1, 7, infinity, infinity, infinity },
new int[]{ infinity, 7, infinity, 0, 2, infinity, 3, infinity, infinity },
new int[]{ infinity, 5, 1, 2, 0, 3, 6, 9, infinity },
new int[]{ infinity, infinity, 7, infinity, 3, 0, infinity, 5, infinity },
new int[]{ infinity, infinity, infinity, 3, 6, infinity, 0, 2, 7 },
new int[]{ infinity, infinity, infinity, infinity, 9, 5, 2, 0, 4 },
new int[]{ infinity, infinity, infinity, infinity, infinity, infinity, 7, 4, 0 },
}; Dijkstra(graph, 0, numberOfVertexes);
} /// <summary>
/// 源点到图中各顶点的最短路径。
/// </summary>
/// <param name="graph">图G。</param>
/// <param name="initialVertex">源点(图G中顶点的下标),图中任意顶点都可以是源点。</param>
/// <param name="numberOfVertexes">图G中顶点的数目。</param>
static void Dijkstra(int[][] graph, int initialVertex, int numberOfVertexes)
{
/**
* 源点到以数组paths的下标为下标的顶点的最短路径
* 的长度(或权重累加和)。
* 比如:paths[2],表示源点到顶点v2的最短路径。
*/
// 0.初始化
Path[] paths = new Path[numberOfVertexes]; /**
* 每条路径设为初始值。
*/
for (int i = 0; i < numberOfVertexes; i++)
{
paths[i] = new Path()
{
Length = Constants.Infinity,
Predecessor = -1,
IsVisited = false
};
} int k = initialVertex; // 从源点开始寻找最短路径。
paths[k].Length = 0; // 源点->源点的路径为0。
paths[k].Predecessor = k; // 源点->源点的路径的前驱(上一个)顶点就是源点。如:(v1, v1)。 /**
* 图G有n个顶点。需要以图中各顶点作为最短路径的立足
* 点探索最短路径。
*/
// 逐个顶点探索最短路径。
for (int i = 0; i < numberOfVertexes; i++)
{
paths[k].IsVisited = true; // 1.以Vk为立足点探索它到其余顶点的最短路径。 /**
* 2.探索Vk的最短路径。从Vk到其余各与Vk相关联的顶点。
*/
for (int j = 0; j < numberOfVertexes; j++)
{
/**
* 若
* 1.paths[j]对应的终点未曾作为立足点。(Vj未曾作为立足点。)
* 2.存在边(Vk, Vj)。
* 3.当前最短路径paths[k]的终点Vk到Vj的路径比已经探索到的源点到Vj的路径paths[j]还更短。
* 则需要更新paths[j],即发现路一条到vj的新路径且比已知长度更短。
*/
if (paths[j].IsVisited == false &&
graph[k][j] != Constants.Infinity &&
(paths[k].Length + graph[k][j] < paths[j].Length))
{
// 更新源点到vj的路径(paths[k]是源点到vk的最短路径)。
paths[j].Length = paths[k].Length + graph[k][j];
// 路径j的上一个顶点应该更新为k(即源点到vj是经过vk到达vj的)。
paths[j].Predecessor = k;
}
} /**
* 3.寻找图G中已知的最短路径。并以该路径的终点为新的立足点探索最短路径。
* 新立足点Vk,其需满足以下条件:
* 1.未曾作为立足点,即paths[k].IsVisited为false。
* 2.路径最小,即paths[k].Length为Min(paths[0].Length, ..., paths[n-1].Length)
*/
int min = Constants.Infinity; // 设当前最小值为无穷。
for (int j = 0; j < numberOfVertexes; j++)
{
if (paths[j].IsVisited) // 若曾作为立足点,则跳过并转向下一个。
continue;
if (paths[j].Length < min) // 发现更小的路径:
{
k = j; // 记录下顶点下标(编号)。
min = paths[j].Length; // 记录下最小路径。
}
} // 在paths[k]处找到最小路径。
} // 输出结果
PrintResult(paths, initialVertex);
} static void DijkstraSimplified(int[][] graph, int initialVertex, int numberOfVertexes)
{
/**
* 源点到以数组paths的下标为下标的顶点的最短路径
* 的长度(或权重累加和)。
* 比如:paths[2],表示源点到顶点v2的最短路径。
*/
// 0.初始化(转换为数组,而不用类。)
//int[] paths = new int[numberOfVertexes];
int[] lengths = new int[numberOfVertexes];
int[] predecessors = new int[numberOfVertexes];
bool[] isVisiteds = new bool[numberOfVertexes]; //Path[] paths = new Path[numberOfVertexes]; /**
* 每条路径设为初始值。
*/
for (int i = 0; i < numberOfVertexes; i++)
{
lengths[i] = Constants.Infinity;
predecessors[i] = -1;
isVisiteds[i] = false; } int k = initialVertex; // 从源点开始寻找最短路径。
lengths[k] = 0; // 源点->源点的路径为0。
predecessors[k] = k; // 源点->源点的路径的前驱(上一个)顶点就是源点。如:(v1, v1)。 /**
* 图G有n个顶点。需要以图中各顶点作为最短路径的立足
* 点探索最短路径。
*/
// 逐个顶点探索最短路径。
for (int i = 0; i < numberOfVertexes; i++)
{
// 1.以Vk为立足点探索它到其余顶点的最短路径。
isVisiteds[k] = true; /**
* 2.探索Vk的最短路径。从Vk到其余各与Vk相关联的顶点。
*/
for (int j = 0; j < numberOfVertexes; j++)
{
/**
* 若
* 1.paths[j]对应的终点未曾作为立足点。(Vj未曾作为立足点。)
* 2.存在边(Vk, Vj)。
* 3.当前最短路径paths[k]的终点Vk到Vj的路径比已经探索到的源点到Vj的路径paths[j]还更短。
* 则需要更新paths[j],即发现路一条到vj的新路径且比已知长度更短。
*/
if (isVisiteds[j] == false &&
graph[k][j] != Constants.Infinity &&
(lengths[k] + graph[k][j] < lengths[j]))
{
// 更新源点到vj的路径(paths[k]是源点到vk的最短路径)。
lengths[j] = lengths[k] + graph[k][j];
// 路径j的上一个顶点应该更新为k(即源点到vj是经过vk到达vj的)。
predecessors[j] = k;
}
} /**
* 3.寻找图G中已知的最短路径。并以该路径的终点为新的立足点探索最短路径。
* 新立足点Vk,其需满足以下条件:
* 1.未曾作为立足点,即paths[k].IsVisited为false。
* 2.路径最小,即paths[k].Length为Min(paths[0].Length, ..., paths[n-1].Length)
*/
int min = Constants.Infinity; // 设当前最小值为无穷。
for (int j = 0; j < numberOfVertexes; j++)
{
if (isVisiteds[j]) // 若曾作为立足点,则跳过并转向下一个。
continue;
if (lengths[j] < min) // 发现更小的路径:
{
k = j; // 记录下顶点下标(编号)。
min = lengths[j]; // 记录下最小路径。
}
} // 在paths[k]处找到最小路径。
} // 输出结果
for (int i = 0; i < numberOfVertexes; i++)
{
string result = $"";
int cursor = i; if (cursor == initialVertex)
{
result = $"->{cursor}";
} while (cursor != initialVertex)
{
result = $"->{cursor}{result}";
cursor = predecessors[cursor];
}
result = $"{cursor}{result}: {lengths[i]}";
Console.WriteLine(result);
}
} static void PrintResult(Path[] paths, int initialVertex)
{
int numberOfVertexes = paths.Length; for (int i = 0; i < numberOfVertexes; i++)
{
string result = $"";
int cursor = i; if (cursor == initialVertex)
{
result = $"->{cursor}";
} while (cursor != initialVertex)
{
result = $"->{cursor}{result}";
cursor = paths[cursor].Predecessor;
}
result = $"{cursor}{result}: {paths[i].Length}";
Console.WriteLine(result);
}
} static void PrintArray(int[] array)
{
Console.Write("[ ");
for (int i = 0; i < array.Length - 1; i++) // 输出数组的前面n-1个
{
Console.Write($"{ToInfinity(array[i])}, ");
}
if (array.Length > 0) // 输出数组的最后1个
{
int n = array.Length - 1;
Console.Write($"{ToInfinity(array[n])}");
}
Console.WriteLine(" ]");
} static string ToInfinity(int i) => i == int.MaxValue ? "∞" : i.ToString();
} /**
* 路径类。源点到图中其余顶点vk的最短路径。
*/
public class Path
{
// 源点到顶点vk的路径长度。(途径的各边的权值之和。该值最终即是最短路径长度。)
public int Length { get; set; } = Constants.Infinity;
// 路径终点途经的上一个顶点(的下标)。
public int Predecessor { get; set; } = -1;
// 路径终点是否曾作为立足点。
public bool IsVisited { get; set; } = false;
} /**
* 表示常量的类。
*/
public static class Constants
{
public static int Infinity { get => int.MaxValue; }
}
} /**
运行结果:
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
*/

TypeScript代码

const infinity: number = Number.MAX_VALUE;

/**
* 路径类。源点到图中其余顶点vk的最短路径。
*/
class Path {
// 源点到顶点vk的路径长度。(途径的各边的权值之和。该值最终即是最短路径长度。)
Length: number = 0;
// 路径终点途经的上一个顶点(的下标)。
Predecessor: number = -1;
// 路径终点是否曾作为立足点。
IsVisited: boolean = false;
} /**
* 源点到图中各顶点的最短路径。
* @param graph 图G。
* @param initialVertex 源点(图G中顶点的下标),图中任意顶点都可以是源点。
* @param numberOfVertexes 图G中顶点的数目。
* @author kokiafan
*/
function dijkstra(graph: number[][], initialVertex: number, numberOfVertexes: number): void {
/**
* 源点到以数组paths的下标为下标的顶点的最短路径
* 的长度(或权重累加和)。
* 比如:paths[2],表示源点到顶点v2的最短路径。
*/
// 0.初始化
let paths: Path[] = []; /**
* 每条路径设为初始值。
*/
for (let i = 0; i < numberOfVertexes; i++) {
paths[i] = new Path();
paths[i].Length = infinity;
paths[i].Predecessor = -1;
paths[i].IsVisited = false;
} let k: number = initialVertex; // 从源点开始寻找最短路径。
paths[k].Length = 0; // 源点->源点的路径为0。
paths[k].Predecessor = k; // 源点->源点的路径的前驱(上一个)顶点就是源点。如:(v1, v1)。
/**
* 图G有n个顶点。需要以图中各顶点作为最短路径的立足
* 点探索最短路径。
*/
// 逐个顶点探索最短路径。
for (let i = 0; i < numberOfVertexes; i++) {
// 1.以Vk为立足点探索它到其余顶点的最短路径。
paths[k].IsVisited = true; /**
* 2.探索Vk的最短路径。从Vk到其余各与Vk相关联的顶点。
*/
for (let j = 0; j < numberOfVertexes; j++) {
/**
* 若
* 1.paths[j]对应的终点未曾作为立足点。(Vj未曾作为立足点。)
* 2.存在边(Vk, Vj)。
* 3.当前最短路径paths[k]的终点Vk到Vj的路径比已经探索到的源点到Vj的路径paths[j]还更短。
* 则需要更新paths[j],即发现路一条到vj的新路径且比已知长度更短。
*/
if (paths[j].IsVisited == false &&
graph[k][j] != infinity &&
(paths[k].Length + graph[k][j] < paths[j].Length)) {
// 更新源点到vj的路径(paths[k]是源点到vk的最短路径)。
paths[j].Length = paths[k].Length + graph[k][j];
// 路径j的上一个顶点应该更新为k(即源点到vj是经过vk到达vj的)。
paths[j].Predecessor = k;
}
} /**
* 3.寻找图G中已知的最短路径。并以该路径的终点为新的立足点探索最短路径。
* 新立足点Vk,其需满足以下条件:
* 1.未曾作为立足点,即paths[k].IsVisited为false。
* 2.路径最小,即paths[k].Length为Min(paths[0].Length, ..., paths[n-1].Length)
*/
let min: number = infinity; // 设当前最小值为无穷。
for (let j = 0; j < numberOfVertexes; j++) {
if (paths[j].IsVisited) // 若曾作为立足点,则跳过并转向下一个。
continue;
if (paths[j].Length < min) // 发现更小的路径:
{
k = j; // 记录下顶点下标(编号)。
min = paths[j].Length; // 记录下最小路径。
}
} // 在paths[k]处找到最小路径。
} // 输出结果
console.log(printResult(paths, initialVertex));
} function dijkstraSimplified(graph: number[][], initialVertex: number, numberOfVertexes: number): void {
/**
* 源点到以数组paths的下标为下标的顶点的最短路径
* 的长度(或权重累加和)。
* 比如:paths[2],表示源点到顶点v2的最短路径。
*/
// 0.初始化(转换为数组,而不用类。)
let lengths: number[] = [];
let predecessors: number[] = [];
let isVisiteds: boolean[] = []; //Path[] paths = new Path[numberOfVertexes]; /**
* 每条路径设为初始值。
*/
for (let i = 0; i < numberOfVertexes; i++) {
lengths[i] = infinity;
predecessors[i] = -1;
isVisiteds[i] = false;
} let k: number = initialVertex; // 从源点开始寻找最短路径。
lengths[k] = 0; // 源点->源点的路径为0。
predecessors[k] = k; // 源点->源点的路径的前驱(上一个)顶点就是源点。如:(v1, v1)。 /**
* 图G有n个顶点。需要以图中各顶点作为最短路径的立足
* 点探索最短路径。
*/
// 逐个顶点探索最短路径。
for (let i = 0; i < numberOfVertexes; i++) {
// 1.以Vk为立足点探索它到其余顶点的最短路径。
isVisiteds[k] = true; /**
* 2.探索Vk的最短路径。从Vk到其余各与Vk相关联的顶点。
*/
for (let j = 0; j < numberOfVertexes; j++) {
/**
* 若
* 1.paths[j]对应的终点未曾作为立足点。(Vj未曾作为立足点。)
* 2.存在边(Vk, Vj)。
* 3.当前最短路径paths[k]的终点Vk到Vj的路径比已经探索到的源点到Vj的路径paths[j]还更短。
* 则需要更新paths[j],即发现路一条到vj的新路径且比已知长度更短。
*/
if (isVisiteds[j] == false &&
graph[k][j] != infinity &&
(lengths[k] + graph[k][j] < lengths[j])) {
// 更新源点到vj的路径(paths[k]是源点到vk的最短路径)。
lengths[j] = lengths[k] + graph[k][j];
// 路径j的上一个顶点应该更新为k(即源点到vj是经过vk到达vj的)。
predecessors[j] = k;
}
} /**
* 3.寻找图G中已知的最短路径。并以该路径的终点为新的立足点探索最短路径。
* 新立足点Vk,其需满足以下条件:
* 1.未曾作为立足点,即paths[k].IsVisited为false。
* 2.路径最小,即paths[k].Length为Min(paths[0].Length, ..., paths[n-1].Length)
*/
let min: number = infinity; // 设当前最小值为无穷。
for (let j = 0; j < numberOfVertexes; j++) {
if (isVisiteds[j]) // 若曾作为立足点,则跳过并转向下一个。
continue;
if (lengths[j] < min) // 发现更小的路径:
{
k = j; // 记录下顶点下标(编号)。
min = lengths[j]; // 记录下最小路径。
}
} // 在paths[k]处找到最小路径。
} // 输出结果
for (let i = 0; i < numberOfVertexes; i++) {
let result: string = "";
let cursor: number = i; if (cursor == initialVertex) {
result = `->${cursor}`;
} while (cursor != initialVertex) {
result = `->${cursor}${result}`;
cursor = predecessors[cursor];
}
result = `${cursor}${result}: ${lengths[i]}`;
console.log(result);
}
} function printResult(paths: Path[], initialVertex: number): string { let numberOfVertexes = paths.length; let result: string = ""; for (let i = 0; i < numberOfVertexes; i++) {
let line: string = "";
let cursor = i; if (cursor === initialVertex) {
line = `->${cursor}`;
} while (cursor != initialVertex) {
line = `->${cursor}${line}`;
cursor = paths[cursor].Predecessor;
}
line = `${cursor}${line}: ${paths[i].Length}`;
result = result.concat(line, "\n");
} return result;
} function Main() {
let numberOfVertexes: number = 9; let graph: number[][] = [
[0, 1, 5, infinity, infinity, infinity, infinity, infinity, infinity],
[1, 0, 3, 7, 5, infinity, infinity, infinity, infinity],
[5, 3, 0, infinity, 1, 7, infinity, infinity, infinity],
[infinity, 7, infinity, 0, 2, infinity, 3, infinity, infinity],
[infinity, 5, 1, 2, 0, 3, 6, 9, infinity],
[infinity, infinity, 7, infinity, 3, 0, infinity, 5, infinity],
[infinity, infinity, infinity, 3, 6, infinity, 0, 2, 7],
[infinity, infinity, infinity, infinity, 9, 5, 2, 0, 4],
[infinity, infinity, infinity, infinity, infinity, infinity, 7, 4, 0],
]; dijkstra(graph, 5, numberOfVertexes);
dijkstraSimplified(graph, 5, numberOfVertexes);
} Main(); /**
运行结果:
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
*/

参考资料:

《大话数据结构》 - 程杰 著 - 清华大学出版社

《我的第一本算法树》 - 宫崎修一 & 石田保辉 著 - 人民邮电出版社 或 《算法动画图解》iOS App

狄克斯特拉(Dijkstra)算法的更多相关文章

  1. 【算法】狄克斯特拉算法(Dijkstra’s algorithm)

    狄克斯特拉算法(Dijkstra’s algorithm) 找出最快的路径使用算法——狄克斯特拉算法(Dijkstra’s algorithm). 使用狄克斯特拉算法 步骤 (1) 找出最便宜的节点, ...

  2. 关于狄克斯特拉算法(dijkstra)总结

    1,2,4是四个定点其他的是距离,从2到4最直接的就是2-4,但是不是最近的,需要舒展一下2-1-4,这样只有8.所以才是最短的.这个过程就是狄克斯特拉算法.下面进入正题:   我们这里定义图的编号为 ...

  3. 狄克斯特拉算法(Python实现)

    概述 狄克斯特拉算法--用于在加权图中找到最短路径 ps: 广度优先搜索--用于解决非加权图的最短路径问题 存在负权边时--贝尔曼-福德算法 下面是来自维基百科的权威解释. 戴克斯特拉算法(英语:Di ...

  4. 迪杰斯特拉Dijkstra算法介绍

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止. 基本思想 通过Dijk ...

  5. JS实现最短路径之迪杰斯特拉(Dijkstra)算法

    最短路径: 对于网图来说,最短路径是指两个顶点之间经过的边上权值和最少的路径,我们称第一个顶点是源点,最后一个顶点是终点 迪杰斯特拉 ( Dijkstra) 算法是并不是一下子就求出 了 Vo 到V8 ...

  6. 最短路径算法-迪杰斯特拉(Dijkstra)算法在c#中的实现和生产应用

    迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先遍历思想),直到扩展到终点为止 贪心算法(Greedy ...

  7. C# 迪杰斯特拉(Dijkstra)算法

    Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止. 其基本思想是,设置顶点集合S并不断地作 ...

  8. 最短路径 - 迪杰斯特拉(Dijkstra)算法

    对于网图来说,最短路径,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点为源点,最后一个顶点为终点.最短路径的算法主要有迪杰斯特拉(Dijkstra)算法和弗洛伊德(Floyd ...

  9. 图的最短路径---迪杰斯特拉(Dijkstra)算法浅析

    什么是最短路径 在网图和非网图中,最短路径的含义是不一样的.对于非网图没有边上的权值,所谓的最短路径,其实就是指两顶点之间经过的边数最少的路径. 对于网图,最短路径就是指两顶点之间经过的边上权值之和最 ...

随机推荐

  1. C语言之简易了解程序环境

    C语言之简易了解程序环境 大纲: 程序的翻译环境 预编译 编译 汇编 链接 程序的运行环境 在ANSI C的任何一种实现中,存在两个不同的环境. 第1种是翻译环境,在这个环境中源代码被转换为可执行的机 ...

  2. IndexError: list index out of range Python常见错误

    引用超过list最大索引,此错误非常常见,注意列表的元素个数 ----------------------------------------------

  3. 《基于Kubernetes舵手集群的设计与实现》

    前言 <基于Kubernetes舵手集群的设计与实现>是我的毕业设计项目.本系统采用Kubernetes容器编排.基于Jenkins\Gitlab的CICD技术.EFK日志收集.Prome ...

  4. 2020牛客NOIP赛前集训营-普及组(第二场)A-面试

    面 试 面试 面试 题目描述 牛牛内推了好多人去牛客网参加面试,面试总共分四轮,每轮的面试官都会对面试者的发挥进行评分.评分有 A B C D 四种.如果面试者在四轮中有一次发挥被评为 D,或者两次发 ...

  5. java面试-JDK自带的JVM 监控和性能分析工具用过哪些?

    一.JDK的命令行工具 1.jps(JVM Process Status Tools):虚拟机进程状况工具 jps -l 2.jinfo(Configuration Info for java):Ja ...

  6. Spring Cloud微服务限流之Sentinel+Apollo生产实践

    Sentinel概述 在基于Spring Cloud构建的微服务体系中,服务之间的调用链路会随着系统的演进变得越来越长,这无疑会增加了整个系统的不可靠因素.在并发流量比较高的情况下,由于网络调用之间存 ...

  7. PAT B1033 旧键盘上的几个键又毁坏了,于是在输入一段文字时,对应得的字符就不会出现。

    题中可用的字母包括大小写(给出的坏键的字母,全为大写),数字,"_" "," "." "-" "+" ...

  8. Redis初学

    1. redis     1. 概念     2. 下载安装     3. 命令操作         1. 数据结构     4. 持久化操作     5. 使用Java客户端操作redis Redi ...

  9. 【CTF】CTFHub 技能树 彩蛋 writeup

    碎碎念 CTFHub:https://www.ctfhub.com/ 笔者入门CTF时时刚开始刷的是bugku的旧平台,后来才有了CTFHub. 感觉不论是网页UI设计,还是题目质量,赛事跟踪,工具软 ...

  10. Go-41-回调

    回调函数,函数有一个参数是函数类型,这个函数就是回调函数 回调函数,可以实现多态,即调用同一个接口,不同的表现,可以实现不同表现 package main import "fmt" ...