认识Dijkstra
艾兹格·维布·迪克斯特拉(Edsger Wybe Dijkstra,/ˈdaɪkstrə/ DYKE-strə;荷兰语:[ˈɛtsxər ˈʋibə ˈdɛikstra] 1930年5月11日-2002年8月6日)是一位荷兰计算机科学家、程序员、软件工程师、系统科学家和科学散文家。他因对开发结构化编程语言做出的基础贡献而获得了1972年的图灵奖,并担任德克萨斯大学奥斯汀分校的斯伦贝谢百年计算机科学主席,任职时间从1984年到2000年。在他于2002年去世前不久,他因其在程序计算的自稳定性方面的工作而获得了ACM PODC分布式计算有影响力论文奖。为了纪念他,该年度奖项在接下来的一年更名为迪克斯特拉奖。
迪克斯特拉在计算机科学领域的贡献
- 最短路径算法,也称为迪克斯特拉算法,现代计算机科学本科课程中广泛教授
- Shunting yard算法
- THE OS 操作系统
- 银行家算法
- 用于协调多个处理器和程序的信号量构造
- 在分布式计算领域提出概念:自稳定性
认识Dijkstra 算法
Dijkstra算法是一种用于解决单源最短路径问题的经典算法,由荷兰计算机科学家Edsger W. Dijkstra于1956年提出。该算法能够找出从一个节点到其他所有节点的最短路径。
需要注意的是,Dijkstra算法要求图中的边权重必须为非负数,否则无法保证得到正确的最短路径。如果图中存在负权边,可以使用其他算法如Bellman-Ford算法来解决最短路径问题。
Dijkstra算法描述
- 将所有顶点标记为未访问。创建一个未访问顶点的集合。
- 为每个顶点分配一个临时距离值
-
- 对于我们的初始顶点,将其设置为零
- 对于所有其他顶点,将其设置为无穷大。
- 每次选择最小临时距离的未访问顶点,作为新的当前顶点
- 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小
-
- 例如,1->6 的距离是 14,而1->3->6 的距离是11。这时将距离更新为 11
- 否则,将保留上次距离值
- 当前顶点的邻居处理完成后,把它从未访问集合中删除
/**
* 迪杰斯特拉算法 - 计算图的最短路径
*
* @param graph 图
* @param v 初始顶点
*/
private static void dijkstra(List<Vertex> graph, Vertex v) {
// 1.将所有顶点标记为未访问,创建一个未访问顶点的集合
List<Vertex> list = new ArrayList<>(graph);
// 2.为每个顶点分配一个临时距离值
// 2.1:对于初始顶点,临时距离值为0;
v.dict = 0;
while (!list.isEmpty()) {
// 3.选择最小临时距离的未访问顶点作为新的当前顶点【选取当前节点】
Vertex curr = chooseMinDictVertex(list);
// 4.对于当前顶点,遍历所有未访问的邻居,并更新它们的临时距离为更小
updateNeighboursDict(curr);
// 5.当前顶点的邻居处理完成后,把它从未访问集合中删除
list.remove(curr);
// 表示当前节点已被访问
curr.visited = true;
}
for (Vertex vertex : graph) {
System.out.println(vertex.getName() + "::" + vertex.dict + "::" + (vertex.prev != null ? vertex.prev.name : "null"));
}
}
/**
* 选择最小临时距离的未访问顶点作为新的当前顶点【可优化,使用优先级队列,参考下面的代码】
* @param list 未访问顶点集合
* @return 临时距离最小的顶点
*/
private static Vertex chooseMinDictVertex(List<Vertex> list) {
// 默认未访问顶点集合中第一个顶点的距离最小
Vertex min = list.get(0);
// 遍历未访问顶点集合,如果发现有距离更小的顶点,则更新距离最小的顶点引用
for (int i = 1; i < list.size(); i++) {
if (list.get(i).dict < min.dict) {
min = list.get(i);
}
}
return min;
}
/**
* 对于当前顶点,遍历所有未访问的邻居,并更新它们的临时距离为更小
* @param curr 当前顶点
*/
private static void updateNeighboursDict(Vertex curr) {
for (Edge edge : curr.edges) {
// 获取邻居顶点
Vertex neighbour = edge.linked;
// 如果邻居顶点未访问(任然存在于未访问集合中)
if (!neighbour.visited) {
// 计算从当前顶点到邻居的距离
int dict = curr.dict + edge.weight;
// 如果这个距离小于之前邻居顶点中维护的距离值,则更新这个距离
if (dict < neighbour.dict) {
neighbour.dict = dict;
// 记录当前节点的路径从何而来
neighbour.prev = curr;
}
}
}
}
改进 - 优先级队列
- 创建一个优先级队列,放入所有顶点(队列大小会达到边的数量)
- 为每个顶点分配一个临时距离值
-
- 对于我们的初始顶点,将其设置为零
- 对于所有其他顶点,将其设置为无穷大。
- 每次选择最小临时距离的未访问顶点,作为新的当前顶点
- 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小,若距离更新需加入队列
-
- 例如,1->6 的距离是 14,而1->3->6 的距离是11。这时将距离更新为 11
- 否则,将保留上次距离值
- 当前顶点的邻居处理完成后,把它从队列中删除
使用优先级队列优化上面步骤三的部分
/**
* 迪杰斯特拉算法 - 计算图的最短路径
*
* @param graph 图
* @param v 初始顶点
*/
private static void dijkstra(List<Vertex> graph, Vertex v) {
// 1.创建一个优先级队列(默认小顶堆),放入所有顶点(队列大小会达到边的数量)
PriorityQueue<Vertex> queue = new PriorityQueue<>(Comparator.comparing(vertex -> vertex.dict));
for (Vertex vertex : graph) {
queue.offer(vertex);
}
// 2.为每个顶点分配一个临时距离值
// 2.1:对于初始顶点,临时距离值为0;
v.dict = 0;
while (!queue.isEmpty()) {
// 3.选择最小临时距离的未访问顶点作为新的当前顶点
Vertex curr = queue.peek();
// 4.对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小,若距离更新需加入队列
if (!curr.visited) {
updateNeighboursDict(curr, queue);
// 表示当前节点已被访问
curr.visited = true;
}
// 5.当前顶点的邻居处理完成后,把它从未访问集合中删除
queue.poll();
}
graph.forEach(vertex -> System.out.println(vertex.name + "::" + vertex.dict + "::" + (vertex.prev != null ? vertex.prev.name : "null")));
}
/**
* 对于当前顶点,遍历其所有未访问的邻居,并更新它们的临时距离为更小,若距离更新需加入队列
*
* @param curr 当前顶点
* @param queue 优先级【按照顶点距离】队列
*/
private static void updateNeighboursDict(Vertex curr, PriorityQueue<Vertex> queue) {
for (Edge edge : curr.edges) {
// 获取邻居顶点
Vertex neighbour = edge.linked;
// 如果邻居顶点未访问(任然存在于未访问集合中)
if (!neighbour.visited) {
// 计算从当前顶点到邻居的距离
int dict = curr.dict + edge.weight;
// 如果这个距离小于之前邻居顶点中维护的距离值,则更新这个距离
if (dict < neighbour.dict) {
neighbour.dict = dict;
neighbour.prev = curr;
// 更新距离需要加入队列
queue.offer(neighbour);
}
}
}
}
Dijkstra 算法存在的问题
按照 Dijkstra 算法,得出
- v1 -> v2 最短距离2
- v1 -> v3 最短距离1
- v1 -> v4 最短距离2
事实应当是
- v1 -> v2 最短距离2
- v1 -> v3 最短距离0
- v1 -> v4 最短距离1
Dijkstra 算法无法处理带有负权边的图的最短路径问题