戴克斯特拉算法(英语:Dijkstra's algorithm,又译迪杰斯特拉算法)由荷兰计算机科学家艾兹赫尔·戴克斯特拉在1956年提出。戴克斯特拉算法使用了广度优先搜索解决赋权有向图的单源最短路径问题。该算法存在很多变体;戴克斯特拉的原始版本找到两个顶点之间的最短路径,但是更常见的变体固定了一个顶点作为源节点然后找到该顶点到图中所有其它节点的最短路径,产生一个最短路径树。该算法常用于路由算法或者作为其他图算法的一个子模块。举例来说,如果图中的顶点表示城市,而边上的权重表示城市间开车行经的距离,该算法可以用来找到两个城市之间的最短路径。
该算法的输入包含了一个有权重的有向图 G,以及G中的一个来源顶点 S。我们以 V 表示 G 中所有顶点的集合。每一个图中的边,都是两个顶点所形成的有序元素对。(u, v) 表示从顶点 u 到 v 有路径相连。我们以 E 表示G中所有边的集合,而边的权重则由权重函数 w: E → [0, ∞] 定义。因此,w(u, v) 就是从顶点 u到顶点 v 的非负权重(weight)。边的权重可以想像成两个顶点之间的距离。任两点间路径的权重,就是该路径上所有边的权重总和。已知 V 中有顶点 s 及 t,Dijkstra 算法可以找到 s 到 t 的最低权重路径(例如,最短路径)。这个算法也可以在一个图中,找到从一个顶点 s 到任何其他顶点的最短路径。
最初的戴克斯特拉算法不采用最小优先级队列,时间复杂度是{\displaystyle O(|V|^{2})}(其中{\displaystyle |V|}为图的顶点个数)。通过斐波那契堆实现的戴克斯特拉算法时间复杂度是{\displaystyle O(|E|+|V|\log |V|)} (其中{\displaystyle |E|}是边数) (Fredman & Tarjan 1984)。对于不含负权的有向图,这是目前已知的最快的单源最短路径算法。
算法描述
这个算法是通过为每个顶点 v 保留目前为止所找到的从s到v的最短路径来工作的。初始时,原点 s 的路径权重被赋为 0 (d[s] = 0)。若对于顶点 m 存在能直接到达的边(s,m),则把d[m]设为w(s, m),同时把所有其他(s不能直接到达的)顶点的路径长度设为无穷大,即表示我们不知道任何通向这些顶点的路径(对于所有顶点的集合 V 中的任意顶点 v, 若 v 不为 s 和上述 m 之一, d[v] = ∞)。当算法结束时,d[v] 中存储的便是从 s 到 v的最短路径,或者如果路径不存在的话是无穷大。
边的拓展是Dijkstra 算法的基础操作:如果存在一条从 u 到 v 的边,那么从 s 到 v 的最短路径可以通过将边(u, v)添加到从 s 到 u 的路径尾部来拓展一条从 s 到 v 的路径。这条路径的长度是 d[u] + w(u, v)。如果这个值比目前已知的 d[v] 的值要小,我们可以用新值来替代当前 d[v] 中的值。拓展边的操作一直运行到所有的 d[v] 都代表从 s 到 v 的最短路径的长度值。此算法的组织令 d[u] 达到其最终值时,每条边(u, v)都只被拓展一次。
算法维护两个顶点集合 S 和 Q。集合 S 保留所有已知最小 d[v] 值的顶点 v ,而集合 Q 则保留其他所有顶点。集合S初始状态为空,而后每一步都有一个顶点从 Q 移动到 S。这个被选择的顶点是 Q 中拥有最小的 d[u] 值的顶点。当一个顶点 u 从 Q 中转移到了 S 中,算法对 u 的每条外接边 (u, v) 进行拓展。
下面的伪代码计算并保留图G中原点s到每一顶点v的最短距离d[v],同时找出并保留v在此最短路径上的“前趋”,即沿此路径由s前往v,到达v之前所到达的顶点。其中,函数Extract_Min(Q) 将顶点集合Q中有最小d[u]值的顶点u从Q中删除并返回u。
/*这里的思想和最小生成树的思想很像有兴趣的可以看看我之前的博客*/
DIJKSTRA_H头文件
//#pragma once
#ifndef DIJKSRA_H
#define DIJKSRA_H
#include<iostream>
#include<string>
using namespace std;
struct Graph
{
int vex;
int edg;
int ** arc;//链接矩阵
string *path;
};
void createGraph(Graph& g);//生成树
void printGraph(Graph g);//打印树
void MINpath(Graph g,int v);//算法实现
#endif
dijkstra.cpp
#include"Dijksra.h"
#define max 1000000
void createGraph(Graph& g)
{
cout << "请输入顶点数" << endl;
cin >> g.vex;
cout << "请输入边数" << endl;
cin >> g.edg;
//g.information = new string[g.vex];
g.arc = new int *[g.vex];
g.path = new string[g.vex];
int i = 0;
//开辟空间的同时进行名称的初始化
for (i = 0; i < g.vex; i++)
{
g.arc[i] = new int[g.vex];
//g.path = "v" + to_string(v);
//g.information[i] = "v" + to_string(i + 1);
for (int k = 0;k < g.vex;k++)
{
g.arc[i][k] = max;
}
}
cout << "请输入每条边之间的顶点编号,以及该边的权重:" << endl;
int start;
int end;
int weight;
for (i = 0;i < g.edg;i++)
{
cin >> start;
cin >> end;
cin >> weight;
g.arc[start][end] = weight;
//g.arc[end][start] = weight;
}
}
void printGraph(Graph g)
{
int i;
for (i = 0;i < g.vex;i++)
{
for (int j = 0;j < g.vex;j++)
{
if (g.arc[i][j] == max)
cout << "*" << " ";
else
{
cout << g.arc[i][j] << " ";
}
}
cout << endl;
}
}
void MINpath(Graph g, int v)
{
int *cost;
cost = new int[g.vex];
int *flag;
flag = new int[g.vex];
//path = new int[g.vex];
//memset(flag, 0, sizeof(flag));
for (int i = 0;i < g.vex;i++)
{
flag[i] = 0;
g.path[i] = "v" + to_string(v);
}
//初始化数组
for (int i = 0;i < g.vex;i++)
{
cost[i] = g.arc[v][i];
}
for (int k = 1;k < g.vex;k++)
{
int min = max;
int curvex = -1;
for (int j = 0;j < g.vex;j++)
{
if (flag[j] == 0 && cost[j] < min)
{
min = cost[j];
curvex = j;
}
}
if (curvex != -1)
{
flag[curvex] = 1;
for (int i = 0;i < g.vex;i++)
{
if ((g.arc[curvex][i] + min < cost[i]) && (flag[i] != 1))
{
cost[i] = g.arc[curvex][i] + min;
if (g.path[curvex] == "v" + to_string(v))
{
g.path[i] = g.path[curvex] + "-->v" + to_string(curvex)+"-->v"+to_string(i);
}
else
{
g.path[i] = g.path[curvex] + "-->v" + to_string(i);
}
}
}
}
}
for (int i = 0;i < g.vex;i++)
{
if (g.path[i] == "v" + to_string(v))
{
cout << g.path[i] << "-->v" << to_string(i)<<"="<<cost[i] << endl;
}
else
cout << g.path[i]<< "=" << cost[i]<< endl;
}
cout << endl;
}
main.cpp
这里是第0个点到各个顶点的最短距离
#include<iostream>
#include"Dijksra.h"
int main()
{
Graph g;
createGraph(g);
printGraph(g);
MINpath(g,0);
}