我编写了Dijksta算法的这个实现,它在循环while Q is not empty
的每次迭代中,而不是找到队列的最小元素,而是占据队列的头部。
这是我写的代码
#include <stdio.h>
#include <limits.h>
#define INF INT_MAX
int N;
int Dist[500];
int Q[500];
int Visited[500];
int Graph[500][500];
void Dijkstra(int b){
int H = 0;
int T = -1;
int j,k;
Dist[b] = 0;
Q[T+1] = b;
T = T+1;
while(T>=H){
j = Q[H];
Visited[j] = 1;
for (k = 0;k < N; k++){
if(!Visited[k] && Dist[k] > Graph[j][k] + Dist[j] && Graph[j][k] != -1){
Dist[k] = Dist[j]+Graph[j][k];
Q[T+1] = k;
T = T+1;
}
}
H = H+1;
}
}
int main(){
int src,target,m;
int a,w,b,i,j;
scanf("%d%d%d%d",&N,&m,&src,&target);
for(i = 0;i < N;i ++){
for(j = 0;j < N;j++){
Graph[i][j] = -1;
}
}
for(i = 0; i< N; i++){
Dist[i] = INF;
Visited[i] = 0;
}
for(i = 0;i < m; i++){
scanf("%d%d%d",&a,&b,&w);
a--;
b--;
Graph[a][b] = w;
Graph[b][a] = w;
}
Dijkstra(src-1);
if(Dist[target-1] == INF){
printf("NO");
}else {
printf("YES\n%d",Dist[target-1]);
}
return 0;
}
我为我找到的所有测试案例运行了这个,并给出了正确的答案 我的问题是为什么我们需要找到最小值?谁能解释一下用简单的英语?此外,我需要一个测试用例,证明我的代码错误。
答案 0 :(得分:3)
看一下这个样本:
1-(6)-> 2 -(7)->3
\ /
(7) (2)
\ /
4
即。边缘长度为6从1到2,边缘长度7从2到3,边缘长度7从1到4,边缘从4到3.我相信你的算法会认为从1到3的最短路径长度为13通过2,实际上最好的解决方案是长度为9到4。
希望这说清楚。
编辑:抱歉,这个例子没有制作代码。看看这个:8 9 1 3
1 5 6
5 3 2
1 2 7
2 3 2
1 4 7
4 3 1
1 7 3
7 8 2
8 3 2
您的输出为Yes 8
。虽然路径1->7->8->3
仅占用7.此处是ideone
答案 1 :(得分:2)
我认为您的代码具有错误的时间复杂性。您的代码(几乎)比较所有节点对,它们具有二次时间复杂度。
尝试添加10000个具有10000个边的节点,并查看代码是否可以在1秒内执行。
答案 2 :(得分:0)
始终必须找出具有最小距离的未访问顶点,否则您将至少获得一个 边缘错误。例如,考虑以下情况
4 4
1 2 8
2 4 5
1 3 2
3 2 1
(8) (5)
1-----2----4
\ /
(2)\ / (1)
3
我们从vertex 1
distance[1]=0
您访问过vertex 1
后,就放松了vertex 2
和vertex 3
所以现在
distance[2]=8
和distance[3]=2
在此之后,如果我们不选择最小值,而是选择vertex 2
,则会得到
distance[4]=13
,然后选择vertex 3
,将显示
distance[2]=3
因此我们以distance[4]=13
结尾
distance[4]=8
因此,我们应该在Dijkstra的每个阶段中从未访问的项目中选择最小值,可以使用priority_queue
有效地做到这一点。
答案 3 :(得分:0)
如果为以下图形运行算法,则取决于子级的顺序。假设我们正在寻找从1到4的最短路径。
如果您从队列中以1开始,
dist[1] = 0
dist[2] = 21
dist[3] = 0
和seen = {1}
,现在我们用2
和3
推送队列时,如果我们从队列中消耗2,它将生成dist[4] = 51
,seen={1,2}
,{ {1}} 1 q = [
2 ,
,下一次从队列,3,4]
中消耗3
时赢了因为它已经在2
中,所以不会再次添加到队列中。因此,该算法稍后会将距seen
的路径的距离更新为12 + 31 = 43,但是最短路径为32并且位于1->3-5->4
上。
让我用代码示例讨论其他一些方面。假设我们有一个1->3->2->4
的连接列表,其中节点(u,v,w)
的权重为u
,指向v
的加权和定向边。并如下准备图形和边线:
w
graph, edges = {i: set() for i in range(1, N+1)}, dict()
for u,v,w in connection_list:
graph[u].add(v)
edges[(u,v)] = w
上面已经讨论了这一点,对于1到4之间的最短路径,它将给我们错误的结果43,而不是32。
问题不在于将2重新添加到队列中,然后让我们摆脱q = deque([start])
seen = set()
dist = {i:float('inf') for i in range(1, N+1)}
dist[start] = 0
while q:
top = q.pop()
seen.add(top)
for v in graph[top]:
dist[v] = min(dist[v], dist[top] + edges[(top, v)])
if v not in seen:
q.appendleft(v)
和孩子们。
seen
在这种情况下可以使用,但是仅适用于此示例。此算法有两个问题,
while q:
top = q.pop()
seen.add(top)
for v in graph[top]:
dist[v] = min(dist[v], dist[top] + edges[(top, v)])
q.appendleft(v)
而不是结点E
的数量,对于密集图,我们可以假设{{1} }。这就是为什么如果我们通过线性搜索来选择最小的子代,我们必须花费额外的时间来选择最小的子代,结果我们将得到与上述相同的复杂性。但是,如果我们使用优先级队列,则可以将最小搜索减少为V
,而不是O(E) = O(N^2)
。这是代码的线性搜索更新。
O(lgN)
现在,我们知道了我们下次可以记住使用堆来获得最佳Dijkstra算法的思想过程。