戴克斯特拉算法

戴克斯特拉算法（Dijkstra's algorithm），又称迪杰斯特拉算法、Dijkstra算法，是由荷兰计算机科学家艾兹赫尔·戴克斯特拉在1956年发现的算法，并于3年后在期刊上发表。戴克斯特拉算法使用类似广度优先搜索的方法解决赋权图Dijkstra1959的单源最短路径问题。
该算法存在很多变体：戴克斯特拉的原始版本仅适用于找到两个顶点之间的最短路径Dijkstra1959，后来更常见的变体固定了一个顶点作为源结点然后找到该顶点到图中所有其它结点的最短路径，产生一个最短路径树。
该算法解决了图 上带权的单源最短路径问题196–206。具体来说，戴克斯特拉算法设置了一顶点集合，在集合中所有的顶点与源点之间的最终最短路径权值均已确定。算法反复选择最短路径估计最小的点并将加入中。该算法常用于路由算法或者作为其他图算法的一个子模块。举例来说，如果图中的顶点表示城市，而边上的权重表示城市间开车行经的距离，该演算法可以用来找到两个城市之间的最短路径。
应当注意，绝大多数的戴克斯特拉算法不能有效处理带有负权边的图。
戴克斯特拉算法在计算机科学的人工智能等领域也被称为均一开销搜索，并被认为是最良优先搜索的一个特例。

**描述**
上图为戴克斯特拉演算法应用示意图。起点以左下角的红点，目标是右上角的绿点，中间灰色的倒L型为障碍物。蓝色空圈表示"暂定"，用以搜索下一步；已经填充颜色的表示探访过，图中颜色以红到绿，越绿表示离起点越远。所有节点都被均匀探索。
戴克斯特拉算法通过保留目前为止所找到的每个顶点从到的最短路径来工作。初始时，原点的路径权重被赋为 0 （即原点的实际最短路径=0）。同时把所有其他顶点的路径长度设为无穷大，即表示我们不知道任何通向这些顶点的路径。当算法结束时， 中储存的便是从到的最短路径，或者如果路径不存在的话是无穷大。
松弛操作是戴克斯特拉算法的基础操作：如果存在一条从到的边，那么从到的一条新路径是将边添加到从到的路径尾部来拓展一条从到的路径。这条路径的长度是。如果这个值比目前已知的的值要小，那幺可以用这个值来替代当前中的值。松弛边的操作一直执行到所有的都代表从到的最短路径的长度值。
算法维护两个顶点集合和。集合保留所有已知实际最短路径值的顶点，而集合则保留其他所有顶点。集合初始状态为空，而后每一步都有一个顶点从移动到。这个被选择的顶点是中拥有最小的值的顶点。当一个顶点从中转移到了中，算法对的每条外接边进行松弛。
《算法导论》中给出了以下伪代码：该伪代码计算并保留图中原点到每一顶点的最短距离。其中，函数将顶点集合中有最小值的顶点从中删除并返回。
-{}-
1  function Dijkstra(G, w, s)
2   INITIALIZE-SINGLE-SOURCE(G, s)                //实际上的操作是将每个除原点外的顶点的置为无穷大，
3   
4                                   //是顶点的一个优先队列，以顶点的最短路径估计排序
5   while()
6       do           //选取为中最短路径估计最小的顶点
7       
8       for each vertex v 
9            do RELAX(u, v, w)            //松弛成功的结点会被加入到队列中
如果我们只对在和之间寻找一条最短路径的话，我们可以在第5或第6行添加条件如果满足的话终止程序。
在肯尼·罗森所着的《离散数学及其应用》中给出了如下的另一份伪代码：
-{}-
1 procedure Dijkstra(G：边全为正权的图）
2   {G带有顶点和若干边}
3    for  to n
4       
5    
6    
7    while 
8    begin
9          不属于的最小的一个顶点
10        
11        for 所有不属于的顶点
12            if  then 
13    end{从a到z的最短路长度}
在该  算法演示页面 date=20240829120512 ，可以自定义节点，边的权重等信息，然后观察算法每一步的执行过程。

**时间复杂度**
我们可以用大O符号将该算法的运行时间表示为边数和顶点数的函数。
对于任何基于顶点集的实现，算法的运行时间是，其中和分别表示完成键的降序排列时间和从中提取最小键值的时间。
对于没有任何优化的戴克斯特拉算法，实际上等价于每次遍历了整个图的所有结点来找到Q中满足条件的元素（即寻找最小的顶点是的），此外实际上还需要遍历所有的边一遍，因此算法的复杂度是。
对于边数少于的稀疏图来说，可以用邻接表来更有效的实现该算法。
可以使用一个二叉堆或者斐波纳契堆用作优先队列来寻找最小的顶点（）以优化算法。当用到二叉堆的时候，算法所需的时间为，斐波纳契堆能提高一些性能，让算法运行时间达到。然而，使用斐波纳契堆进行编程，有时会由于算法常数过大而导致速度没有显着提高。
下面是一些戴克斯特拉算法经典实现的复杂度比较：

**正确性证明**
艾兹赫尔·戴克斯特拉，戴克斯特拉算法的发现者
戴克斯特拉本人在他的论文中给出了一份简单的证明。
《算法导论》使用循环不变式（数学归纳法）给出了如下的一份证明：
已知一带权图，其加权函数的值非负，源点为。对该图运行戴克斯特拉算法，对所有有。其中表示u点的最短路径估计，表示到点的最短路径。
证明：证明如下的循环不变式成立即可：在每次执行EXTRACT-MIN时，对每个顶点，有成立即可。由于上界性质，在加入了之后，一旦有，则在后面的每次循环中都不会改变这个性质。
初始化：第一次循环前，，因此循环不变式显然成立。
保持：实际上要证明每一轮循环中加入到中的结点满足。利用反证法，假设是第一个不满足此条件的结点，考虑循环开始前的状况，首先一定不等于，这是显然的。其次一定有到的路径，否则路径为无穷大。那幺假设在进入时，有最短路径，假设该路径上存在两个点，。、，且x是y的前驱，路径可以分解为（此处表示经过这条路径，后同），其中路径和路径可以为空。由于是第一个不满足的，又因为是满足该条件的，而且一定已经被松弛过了，所以是满足该条件的。
现在只需要推出矛盾，即可证明u不存在：在之前出现，而且图中所有权值非负，因此有，所以：，但是由于和同时在中，因此，因此必有，也就证明了点不可能不满足该条件，上述假设为假，原命题得证。
终止：终止时，，由于，因此，因此对所有有。

**起源与历史**
戴克斯特拉1956年在荷兰数学和计算机科学研究学会担任程序员时为了展示新型计算机ARMAC的功能曾思考过最短路径问题的解法。他的目标是让不去实际计算的人也能理解这个问题和解决的方法，于是他在发现了这个算法之后在ARMAC上做了简单实验。1959年，他正式将此算法发表在期刊上，该算法也成为了戴克斯特拉成名的基石之一。

**相关应用**
一个多区域OSPF网络，在OSPF中使用本算法计算最短路径
中需要计算最短路时常常要用到该算法，该算法在开放最短路径优先和中间系统到中间系统协议中的相关应用是其在网络路由中的典型实现。
戴克斯特拉算法及其改进算法应用广泛，尤其是在寻路、交通、规划中。
如果有已知信息可用来估计某一点到目标点的距离，则可改用A*搜寻算法，以减小最短路径的搜索范围，戴克斯特拉算法本身也可以看作是A*搜索算法的一个特例。
戴克斯特拉算法本身采用了与Prim算法类似的贪心策略。快速行进算法与戴克斯特拉算法同样有相似之处。

**参考源程序**
以下是该算法使用堆优化的一个C++实现参考：
// 无向图
#include 
using namespace std;
#define INF 0x3f3f3f3f
// iPair ==> Integer Pair（整数对）
typedef pair iPair;
// 加边
void addEdge(vector> adj, int u, int v, int wt)
{
adju.push_back(make_pair(v, wt));
adjv.push_back(make_pair(u, wt));
}
// 计算最短路
void shortestPath(vector> adj, int V, int src)
{
// 关于stl中的优先队列如何实现，参考下方网址：
// http://geeksquiz.com/implement-min-heap-using-stl/
priority_queue, greater> pq;
// 距离置为正无穷大
vector dist(V, INF);
vector visited(V, false);
// 插入源点，距离为0
pq.push(make_pair(0, src));
distsrc = 0;
/* 循环直到优先队列为空 */
while (!pq.empty())
{
// 每次从优先队列中取出顶点事实上是这一轮最短路径权值确定的点
int u = pq.top().second;
pq.pop();
if (visitedu)
{
continue;
}
visitedu = true;
// 遍历所有边
for (auto x : adju)
{
// 得到顶点边号以及边权
int v = x.first;
int weight = x.second;
// 可以松弛
if (distv > distu + weight)
{
// 松弛
distv = distu + weight;
pq.push(make_pair(distv, v));
}
}
}
// 打印最短路
printf("Vertex Distance from Source\n");
for (int i = 0; i  adjV;
addEdge(adj, 0, 1, 4);
addEdge(adj, 0, 7, 8);
addEdge(adj, 1, 2, 8);
addEdge(adj, 1, 7, 11);
addEdge(adj, 2, 3, 7);
addEdge(adj, 2, 8, 2);
addEdge(adj, 2, 5, 4);
addEdge(adj, 3, 4, 9);
addEdge(adj, 3, 5, 14);
addEdge(adj, 4, 5, 10);
addEdge(adj, 5, 6, 2);
addEdge(adj, 6, 7, 1);
addEdge(adj, 6, 8, 6);
addEdge(adj, 7, 8, 7);
shortestPath(adj, V, 0);
return 0;
}
以下是该算法Python的一个实现：
import sys
max = sys.maxsize
vertices_number = 6
adjacency_matrix = 
0, 1, 10, -1, -1, 2,
10, 0, 1, -1, -1, -1,
1, 10, 0, -1, -1, -1,
-1, -1, 2, 0, 1, 10,
-1, -1, -1, 10, 0, 1,
-1, -1, -1, 1, 10, 0
start = 
dest = "2", "5"
key = 
def init_keys(s: int):
global key
key =  max  * vertices_number
keys = 0
def dijkstra(from_vertex, dest_vertex):
fid = int(from_vertex) - 1
tid = int(dest_vertex) - 1
init_keys(fid)
rel = fid
min_vertex = fid
hop_path = {}
while len(rel)  0 \
and keyi > keymin_vertex + adjacency_matrixmin_vertexi:
keyi = keymin_vertex + adjacency_matrixmin_vertexi
hop_path.update({i + 1: {"from": min_vertex + 1, "cost": adjacency_matrixmin_vertexi}})
if min_vertex not in rel:
rel.append(min_vertex)
min_vertex = tid
for i in range(vertices_number):
if i not in rel and keyi  {}".format(str(next_hop), cost ,path_str)
path_str =  "{} -({})-> {}".format(str(hop_pathnext_hop"from"), hop_pathnext_hop"cost", path_str)
return keytid, path_str
def find_shortest_router():
for s in start:
print("Forwarding Table for {}".format(s))
print("{:>10} {:>10}       {}".format("To", "Cost", "Path"))
for d in dest:
c, n = dijkstra(s, d)
print("{:>10} {:>10}       {}".format(d, c, n))
def main():
for i in range(1, vertices_number + 1):
if str(i) not in dest:
start.append(str(i))
find_shortest_router()
if __name__ == '__main__':
main()

**参见**
 图论- A*搜寻演算法- 贝尔曼-福特算法- 宽度优先搜索- Flood fill- Floyd-Warshall算法- 最长路径问题

**参考**

**参考文献**

**扩展阅读**
 cite book  
title = Introduction to Algorithms | edition = second | publisher = MIT Press、S&P Global | year = 2001 | isbn = 0-262-03293-7 | chapter = Section 24.3: Dijkstra's algorithm | pages = 595–601 | ref = harv-  doi = 10.1287/trsc.32.1.65- doi=10.1016/0020-0190(77)90002-3- ref=harv
*doi=10.1145/261342.261352
*ref=harv
*dead-url=yes
*date=2007-05

**外部连结**
  迪科斯彻算法分解演示视频（优酷） date=20210213073618 -  Animation of Dijkstra's algorithm date=20140219102217 -  The Boost Graph Library (BGL) date=20081202234015 -  Interactive Implementation of Dijkstra's Algorithm date=20200217212029 -  Shortest Path Problem: Dijkstra's Algorithm-  Dijkstra算法使用TDD的一个实现 date=20201229133730 -  Graphical explanation of Dijkstra's algorithm step-by-step on an example date=20210317123138