我曾尝试在循环加权图上使用Djikstra的算法,而不使用优先级队列(堆),结果成功了。
Wikipedia指出,该算法的原始实现不使用优先级队列,而是在O(V2)时间内运行。
现在,如果我们只删除优先级队列并使用普通队列,则运行时间是线性的,即O(ve)。
有人能解释一下为什么我们需要优先队列吗?
对于稀疏图,如果使用二进制最小堆实现运行时为(E*logV),但是如果使用斐波那契堆实现它,运行时将为(VlogV E)。
就像Moataz Elmasry所说,您可以期望的最好结果是带有fib队列的O(|E||V|.|logV|)。至少在大oh值方面是这样。
其背后的思想是,对于您当前正在处理的每个顶点(节点),您已经找到了到的最短路径。如果该顶点不是最小的(距离边权重),则不一定是真的。这允许您在展开(?)后立即停止算法从初始顶点可到达的每个顶点。如果不扩展最小顶点,则不能保证找到最短路径,因此必须测试每条路径,而不仅仅是一条。因此,你不必只通过一条路径中的每条边,而是通过每条路径中的每一条边。
您对O(E V)的估计可能是正确的,另一方面,您确定的路径和成本不正确。如果我没有记错的话,路径只会是最短的,如果你从每个顶点行进的第一条边恰好是最小的一条。
因此,没有优先级队列的Dijkstra最短路径算法就是Dijksstra的路径算法;)
我有完全相同的疑问,并发现了一个测试用例,其中没有priority_queue的算法将无法正常工作。
假设我有一个Graph对象g
,一个方法addEdge(a, b, w)
,它将顶点a
的边缘添加到顶点b
具有权重w
。
现在,让我定义以下图表:-
Graph g
g.addEdge(0,1,5) ;
g.addEdge(1,3,1) ;
g.addEdge(0,2,2) ;
g.addEdge(2,1,1) ;
g.addEdge(2,3,7) ;
现在,假设我们的队列包含以下顺序的节点< code>{0,1,2,3 }因此,首先访问节点0,然后访问节点1。
此时,使用路径<代码>0将距离b/w 0和3计算为6-
现在访问节点2并使用路径0-将dist b/w 0和1更新为值3
因此,您的算法在不使用priority_queue的情况下会失败。
它报告dist b/w 0和3为6,而实际上应该是4。
下面是我用来实现算法的代码:-
class Graph
{
public:
vector<int> nodes ;
vector<vector<pair<int,int> > > edges ;
void addNode()
{
nodes.push_back(nodes.size()) ;
vector<pair<int,int> > temp ; edges.push_back(temp);
}
void addEdge(int n1, int n2, int w)
{
edges[n1].push_back(make_pair(n2,w)) ;
}
pair<vector<int>, vector<int> > shortest(int source) // shortest path djkitra's
{
vector<int> dist(nodes.size()) ;
fill(dist.begin(), dist.end(), INF) ; dist[source] = 0 ;
vector<int> pred(nodes.size()) ;
fill(pred.begin(), pred.end(), -1) ;
for(int i=0; i<(int)edges[source].size(); i++)
{
dist[edges[source][i].first] = edges[source][i].second ;
pred[edges[source][i].first] = source ;
}
set<pair<int,int> > pq ;
for(int i=0; i<(int)nodes.size(); i++)
pq.insert(make_pair(dist[i],i)) ;
while(!pq.empty())
{
pair<int,int> item = *pq.begin() ;
pq.erase(pq.begin()) ;
int v = item.second ;
for(int i=0; i<(int)edges[v].size(); i++)
{
if(dist[edges[v][i].first] > dist[v] + edges[v][i].second)
{
pq.erase(std::find(pq.begin(), pq.end(),make_pair(dist[edges[v][i].first],edges[v][i].first))) ;
pq.insert(make_pair(dist[v] + edges[v][i].second,edges[v][i].first)) ;
dist[edges[v][i].first] = dist[v] + edges[v][i].second ;
pred[i] = edges[v][i].first ;
}
}
}
return make_pair(dist,pred) ;
}
pair<vector<int>, vector<int> > shortestwpq(int source) // shortest path djkitra's without priority_queue
{
vector<int> dist(nodes.size()) ;
fill(dist.begin(), dist.end(), INF) ; dist[source] = 0 ;
vector<int> pred(nodes.size()) ;
fill(pred.begin(), pred.end(), -1) ;
for(int i=0; i<(int)edges[source].size(); i++)
{
dist[edges[source][i].first] = edges[source][i].second ;
pred[edges[source][i].first] = source ;
}
vector<pair<int,int> > pq ;
for(int i=0; i<(int)nodes.size(); i++)
pq.push_back(make_pair(dist[i],i)) ;
while(!pq.empty())
{
pair<int,int> item = *pq.begin() ;
pq.erase(pq.begin()) ;
int v = item.second ;
for(int i=0; i<(int)edges[v].size(); i++)
{
if(dist[edges[v][i].first] > dist[v] + edges[v][i].second)
{
dist[edges[v][i].first] = dist[v] + edges[v][i].second ;
pred[i] = edges[v][i].first ;
}
}
}
return make_pair(dist,pred) ;
}
正如预期的那样,结果如下:-
priority_queue
0
3
2
4
现在使用无优先级队列
0
3
2
6
我正在为Dikjstra算法做一个优先级队列。我目前在插入方法上有麻烦。我包含了整个类的代码,以防你需要更好地了解我想完成的事情。我将堆索引放在一个数组列表(heapIndex)中,堆放在另一个数组列表中。 那是我运行程序后的输出(值,优先级,堆索引)。*(-1)表示heapIndex中的空单元格。
这是我写的Dijkstra算法的代码: 在这方面我不能理解的工作 这涉及到: < code>()运算符在这里有什么用?我是说它在这段代码中是如何运作的? 还有为什么我们使用
在我实现Dijkstra算法的过程中,我有1个数组(包含所有节点)和1个优先级队列(包含所有节点)。每当一个节点排队时,我都会用新的距离和它来自哪里来更新所有相邻的节点,这样我就可以回溯路径。 优先级队列中的节点更新为新距离,数组中的节点更新为它来自的位置和新距离。当节点出列时,数组中的最终距离会更新: 用前一个节点的信息更新数组和用距离更新优先级队列是否可以接受? 只要找到更好的距离,就会发生这
我正在使用优先级队列实现Dijkstra的算法,我想要一个函数从堆中删除一个元素,但我只能从Dijkstra的主节点索引中向它发送顶点索引,我找不到它在堆上的位置,我负担不起进行二进制搜索。有什么想法吗?
我在CLRS,第三版(第662页)中读到了Dijkstra的算法。下面是我不明白的书中的一部分: 如果图足够稀疏--特别是-我们可以通过用二进制最小堆实现最小优先级队列来改进算法。 为什么图形应该是稀疏的? 下面是另一部分:
我需要使用Java中的优先级队列实现Dijkstra的算法。这是我到目前为止的代码: 我需要填写最短路径方法并返回节点数组。但是,我不确定如何做到这一点。我知道我需要在某个时候进行优先排队,但有人可以向我解释一下如何吗?我已经制作了startNode,我知道我需要为它分配一个距离值0,其余节点的距离值为无穷大。另外,可比性从何而来?