英文:
When is the shortest path to some vertex found in Dijkstra's algorithm?
问题
- 我已经尝试理解Dijkstra算法并尝试编写自己的实现,有一些事情我不太理解:
- 我什么时候可以将某些顶点标记为已访问,并确信没有更短的路径到达它?
- 我什么时候可以在寻找两个顶点之间的最短路径时停止搜索?
- 此外,一些来源说要使用
decrease-key
而不是将相邻的顶点添加到优先队列中。这是什么意思,它是如何工作的?
英文:
I've tried to understand Dijkstra's algorithm and attempted to write my own implementation, and there are some things I don't understand:
- When can I mark some vertex as visited and be sure that there is no shorter path to it?
- When can I stop searching when looking for the shortest path between two vertices?
What's more, some sources say to use decrease-key
instead of adding adjacent vertices to a priority queue. What does that mean and how does it work?
答案1
得分: 1
Djikstra's algorithm(迪杰斯特拉算法):
初始化所有距离为 "无穷大"。
将起点放入优先队列(队列的前端始终保存着从已确定节点到终点的最短距离)。
循环:
弹出队列的前端(实际上是将其标记为已确定节点) -(如果队列为空,则无法到达所需的终点)。
如果它是所需的终点,则完成。
否则,将从已确定节点访问的所有可改进距离的节点添加到队列中。
考虑以下图示(@ravenspoint 示例,底部线条分开):
请注意,要从 A 到 B 获得最短路径:
B 仅被(明确地)考虑一次。
E 从不被访问 - 您不需要到达所有节点的距离。
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <limits>
using namespace std;
using NodeType = char;
struct Edge
{
NodeType dest;
double wt;
};
bool operator < ( Edge a, Edge b ) { return a.dest < b.dest; }
using Graph = map<NodeType, set<Edge>>;
//======================================================================
void read( istream &in, Graph &graph )
{
graph.clear();
NodeType a, b;
double wt;
while( in >> a >> b >> wt )
{
graph[a].insert( Edge{ b, wt } );
graph[b].insert( Edge{ a, wt } );
}
}
//======================================================================
double Dijkstra( Graph &graph, NodeType start, NodeType finish )
{
const double LARGE = numeric_limits<double>::max();
// Set all distances from start to infinity
map<NodeType,double> dist;
for ( auto n : graph ) dist[n.first] = LARGE;
dist[start] = 0;
// Create a priority queue that will be sorted by distance from source
auto further = [&]( NodeType a, NodeType b ){ return dist[a] > dist[b]; };
priority_queue<NodeType, vector<NodeType>, decltype(further)> Q( further );
// Push all nodes accessible from the start onto the queue
cout << "Finalised " << start << '\n'; // Node with smallest currently-accessible distance
for ( const Edge &edge : graph[start] )
{
dist[edge.dest] = edge.wt;
Q.push( edge.dest );
cout << "Queued " << edge.dest << '\n';
}
// Dijkstra: take the smallest distance amongst those so far accessible and
// finalise it (i.e. pop it from the front of the queue).
while( !Q.empty() )
{
NodeType n = Q.top();
cout << "Finalised " << n << '\n'; // Smallest currently-accessible node
if ( n == finish ) return dist[n]; // If at the target then stop
Q.pop();
for ( const Edge &edge : graph[n] )
{
double test = dist[n] + edge.wt;
if ( dist[edge.dest] > test ) // If we improve a distance, then push onto queue
{
dist[edge.dest] = test;
Q.push(edge.dest);
cout << "Queued " << edge.dest << '\n';
}
}
}
// If we get this far then the target is inaccessible
return -1.0;
}
//======================================================================
int main()
{
istringstream in( "A C 1 \n"
"C B 2 \n"
"A D 10 \n"
"D E 10 \n"
"E B 80 \n" );
// ifstream in( "graph.in" );
// istream &in = cin;
char start = 'A', finish = 'B';
Graph G;
read( in, G );
double d = Dijkstra( G, start, finish );
cout << "\nShortest path from " << start << " to " << finish << " is " << d << '\n';
}
输出:
Finalised A
Queued C
Queued D
Finalised C
Queued B
Finalised B
Shortest path from A to B is 3
英文:
Djikstra's algorithm:
Initialise all distances to "infinity".
Push start onto a priority queue (front of queue will always holdest shortest distance from a finalised node)
Loop:
Pop front of queue (effectively finalising it) - (desired end point is inaccessible if queue is empty here)
If it is the desired end point, then done.
Otherwise add to queue all nodes accessible from that finalised node whose distance could be improved.
Consider the following graph (@ravenspoint example with bottom line split)
Note that, to get from A to B by the shortest path:
B is only (explicitly) considered once.
E is never visited - you do NOT need the distance to all nodes.
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <map>
#include <set>
#include <queue>
#include <limits>
using namespace std;
using NodeType = char;
struct Edge
{
NodeType dest;
double wt;
};
bool operator < ( Edge a, Edge b ) { return a.dest < b.dest; }
using Graph = map< NodeType, set<Edge> >;
//======================================================================
void read( istream &in, Graph &graph )
{
graph.clear();
NodeType a, b;
double wt;
while( in >> a >> b >> wt )
{
graph[a].insert( Edge{ b, wt } );
graph[b].insert( Edge{ a, wt } );
}
}
//======================================================================
double Dijkstra( Graph &graph, NodeType start, NodeType finish )
{
const double LARGE = numeric_limits<double>::max();
// Set all distances from start to infinity
map<NodeType,double> dist;
for ( auto n : graph ) dist[n.first] = LARGE;
dist[start] = 0;
// Create a priority queue that will be sorted by distance from source
auto further = [&]( NodeType a, NodeType b ){ return dist[a] > dist[b]; };
priority_queue< NodeType, vector<NodeType>, decltype(further) > Q( further );
// Push all nodes accessible from the start onto the queue
cout << "Finalised " << start << '\n'; // Node with smallest currently-accessible distance
for ( const Edge &edge : graph[start] )
{
dist[edge.dest] = edge.wt;
Q.push( edge.dest );
cout << "Queued " << edge.dest << '\n';
}
// Dijkstra: take the smallest distance amongst those so far accessible and
// finalise it (i.e. pop it from the front of the queue).
while( !Q.empty() )
{
NodeType n = Q.top();
cout << "Finalised " << n << '\n'; // Smallest currently-accessible node
if ( n == finish ) return dist[n]; // If at the target then stop
Q.pop();
for ( const Edge &edge : graph[n] )
{
double test = dist[n] + edge.wt;
if ( dist[edge.dest] > test ) // If we improve a distance, then push onto queue
{
dist[edge.dest] = test;
Q.push(edge.dest);
cout << "Queued " << edge.dest << '\n';
}
}
}
// If we get this far then the target is inaccessible
return -1.0;
}
//======================================================================
int main()
{
istringstream in( "A C 1 \n"
"C B 2 \n"
"A D 10 \n"
"D E 10 \n"
"E B 80 \n" );
// ifstream in( "graph.in" );
// istream &in = cin;
char start = 'A', finish = 'B';
Graph G;
read( in, G );
double d = Dijkstra( G, start, finish );
cout << "\nShortest path from " << start << " to " << finish << " is " << d << '\n';
}
Output:
Finalised A
Queued C
Queued D
Finalised C
Queued B
Finalised B
Shortest path from A to B is 3
答案2
得分: -1
无法。如果以后找到了更短的路径,那么前驱顶点和距离将会更新。访问状态保持为真。
当你访问了每个顶点时可以停止搜索。请注意,你不能提前停止,因为谁知道,你最后访问的顶点可能有一条通向B的边,它是最短路径的一部分。
例如:
根据边的存储顺序不同,第一次访问B时,路径长度为100。第二次,它会更新为3。
英文:
> When I can mark some vertex as visited and be sure that there is not shorter path to it
You cannot. If a shorter path is found later, then the predecessor vertex and the distance will be updated. The visited status remains true.
> When I can stop searching if I look for shortest path between A and B
When you have visited every vertex. Note that you cannot stop earlier because, who knows, the last vertex you visit could have a edge to B which is part of the shortest path.
For example:
Depending on the order in which the edges are stored, the 1st time B is visited the path length is 100. The second time, it is updated to 3.
You do not mention what coding language you use. Here is a C++ implementation of Dijkstra https://github.com/JamesBremner/PathFinder/blob/f07a29d10b5c14e6c085dd4eb6cb447e24c21eae/src/GraphTheory.cpp#L18-L76
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论