C 迪克斯特拉';s算法:为什么需要在队列中查找最小距离元素
我编写了Dijksta算法的这个实现,它在循环的每次迭代中C 迪克斯特拉';s算法:为什么需要在队列中查找最小距离元素,c,algorithm,graph,shortest-path,dijkstra,C,Algorithm,Graph,Shortest Path,Dijkstra,我编写了Dijksta算法的这个实现,它在循环的每次迭代中而Q不是空的而不是找到队列的最小元素,它占据队列的头部 这是我写的代码 #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;
而Q不是空的而不是找到队列的最小元素,它占据队列的头部
这是我写的代码
#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;
}
#包括
#包括
#定义INF INT_MAX
int N;
国际区[500];
int Q[500];
国际访问[500];
int图[500][500];
迪杰克斯特拉(内部b){
int H=0;
int T=-1;
int j,k;
距离[b]=0;
Q[T+1]=b;
T=T+1;
而(T>=H){
j=Q[H];
访问量[j]=1;
对于(k=0;k图[j][k]+Dist[j]&图[j][k]!=-1){
Dist[k]=Dist[j]+图[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);
对于(i=0;i
我对我找到的所有测试用例都运行了这个,它给出了正确的答案。
我的问题是为什么我们需要找到min?有人能用简单的英语给我解释一下吗?我还需要一个测试用例来证明我的代码是错误的。看看这个示例:
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
您的输出是是8
。而路径1->7->8->3
只需要7。这里有一个链接,我认为你的代码的时间复杂度是错误的。您的代码比较(几乎)所有节点对,这是二次时间复杂度
尝试添加10000个节点和10000条边,看看代码是否能在1秒内执行
始终必须找出距离最小的未访问顶点,否则至少会得到一个顶点
边缘的形状是错误的。例如,考虑下面的案例
4
12 8
2 4 5
1 3 2
3.2.1
(8) (5)
1-----2----4
\ /
(2)\ / (1)
3
我们从顶点1开始
distance[1]=0
当您访问顶点1
时,您放松了顶点2
和顶点3
所以现在
距离[2]=8
和距离[3]=2
在此之后,如果我们不选择最小值,而是选择顶点2,我们得到
距离[4]=13
然后选择顶点3,它将给出
distance[2]=3
因此,我们得到的距离[4]=13
距离[4]=8
因此,我们应该在Dijkstra的每个阶段从未访问中选择最小值,这可以通过使用优先级\u队列
有效地完成。如果为下图运行算法,它取决于子级的顺序。假设我们正在寻找从1到4的最短路径
如果从队列1开始
dist[1] = 0
dist[2] = 21
dist[3] = 0
当使用2
和3
推送队列时,如果我们从队列中消耗2,它将使dist[4]=51
,seen={1,2}
,q=[
1,
2,3,4]
下次从队列中使用3
时,将不会再次将2
添加到队列中,因为它已在中查看。因此,该算法稍后会将距离从1->3-5->4
的路径更新为12+31=43,但最短路径是32,它位于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-如果未访问,则选择要添加的任何子项
上面已经讨论过这个问题,对于1和4之间的最短路径,它将给出错误的结果43,而不是32
问题是没有将2重新添加到队列中,然后让我们将seen
和孩子们再次删除
算法2-再次将所有子级添加到队列中
在这种情况下,这将起作用,但它仅适用于本例。这个算法有两个问题
我们再次添加相同的节点,因此对于更大的示例,复杂性将取决于边的数量E
,而不是节点的数量V
,对于稠密图,我们可以假设O(E)=O(N^2)
如果我们在图中添加循环,它将永远运行,因为没有停止的检查。所以这个算法不适合循环图
所以,这就是为什么我们必须花费额外的时间来选择最小的子对象,如果我们使用线性搜索,我们将得到与上面相同的复杂性。但是如果我们使用优先级队列,我们可以将最小搜索减少到O(lgN)
,而不是O(N)
。下面是代码的线性搜索更新
算法3-带线性最小搜索的Dijkstra算法
现在我们知道了下一次使用堆来获得最佳Dijkstra算法的思维过程。我尝试了这个输入(413\n126\n2 37\n147\n432\n
),这给了我YES\n9\n
。显然这是正确的答案,所以这不是反例。很抱歉你是对的。我正在添加一个有效的示例。我不会画它,因为它非常大。所以问题是节点的数量很重要
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)
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)
q = [K]
seen = set()
dist = {i:float('inf') for i in range(1, N+1)}
dist[start] = 0
while q:
min_dist, top = min((dist[i], i) for i in q)
q.remove(top)
seen.add(top)
for v in graph[top]:
dist[v] = min(dist[v], dist[top] + edges[(top, v)])
if v not in seen:
q.append(v)