【发布时间】:2021-07-18 15:25:45
【问题描述】:
我正在编写 Dijkstra 算法的实现,以了解酷图算法(这不是家庭作业,仅供参考)。我使用 Wikipedia 的 description 算法作为我的主要资源。
我测试了不同的遍历路径,得到以下结果((foo, bar) 表示foo to bar):
crashes:
(e, f)
(f, d)
(c, a)
(f, g)
incorrect:
(a, c)
(g, f)
working:
(d, f)
我正在使用的图表如下所示:
F - H
| |
A ---- B - C
| /
| /
E - G - D
通过跟踪从E 到F 的路径,我大致了解了我的代码失败的原因。另一个问题是我不知道如何使用我的方式来实现算法,否则。这是从E 到F 的跟踪:
在节点E,我的邻居是A 和G。最短的暂定距离是G,所以这是下一个当前节点。 G的邻居是E和D,但是E已经被遍历过了,所以C是下一个。对于C,它的邻居D 被遍历,所以我们现在到达B(B 和H 是等距的,但它在C 的边列表中被首先选择)。这是我的问题所在:
A 的暂定距离已由 E 计算为 2。由于从 B 到 A 的新暂定距离远大于两个,因此它的距离保持在 2。对于F,它的距离设置为暂定距离,因为它被初始化为infinity。 A的距离比较小,所以选择它作为下一个节点。 A的唯一邻居是E和B,它们已经被遍历过了,所以它周围的所有节点都已经被探索过了。变量closest(请参阅下面的代码)被初始化为一个节点,除了distance 的distance 之外,没有其他填充字段@ 987654358@,因此对于下一次迭代,它没有边,并且我得到了分段错误.
我知道这是我的代码中发生的事情,因为它的输出如下所示:
Current: e
New neighbor: a
New neighbor: g
g, closest, distance of 1
Current: g
New neighbor: d
d, closest, distance of 2
Current: d
New neighbor: c
c, closest, distance of 4
Current: c
New neighbor: b
New neighbor: h
b, closest, distance of 5
Current: b
New neighbor: a
New neighbor: f
a, closest, distance of 2
Current: a
?, closest, distance of 1000
Current: ?
Segmentation fault: 11
我在实现这个算法时哪里出错了?我试图非常仔细地遵循维基百科对它的 6 步描述。他们的描述和我的唯一区别是我没有使用集合来跟踪已探索和未探索的节点(相反,数据保存在节点本身中)。请提供您可以提供的任何见解。
注意:我在没有优化的 Mac 上使用 Clang 进行编译 (-O0)。我注意到,通过更高的优化,我的程序会无限重复,然后给我另一个分段错误,但我会优先解决我的算法的核心问题,然后再处理它。
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#define infinity 1000
struct Node {
unsigned char value;
int visited, distance, edge_count;
int* weights, weight_assign_index, freed;
struct Node** edges;
};
typedef struct Node Node;
Node* init_node(const unsigned char value, const int edge_count) {
Node* node = malloc(sizeof(Node));
node -> value = value;
node -> visited = 0;
node -> distance = infinity;
node -> edge_count = edge_count;
node -> weights = malloc(edge_count * sizeof(int));
node -> weight_assign_index = 0;
node -> freed = 0;
node -> edges = malloc(edge_count * sizeof(Node*));
return node;
}
void assign_edges(Node* node, const int amount, ...) {
va_list edges;
va_start(edges, amount);
for (int i = 0; i < amount; i++)
node -> edges[i] = va_arg(edges, Node*);
va_end(edges);
}
void assign_weight(Node* node_1, Node* node_2, const int weight) {
for (int i = 0; i < node_1 -> edge_count; i++) {
if (node_1 -> edges[i] == node_2) {
node_1 -> weights[node_1 -> weight_assign_index++] = weight;
node_2 -> weights[node_2 -> weight_assign_index++] = weight;
}
}
}
void deinit_graph(Node* node) {
if (!node -> freed) {
node -> freed = 1;
free(node -> weights);
for (int i = 0; i < node -> edge_count; i++)
deinit_graph(node -> edges[i]);
free(node -> edges);
}
}
void dijkstra(Node* current, Node* goal) {
Node local_closest;
local_closest.distance = infinity;
Node* closest = &local_closest;
printf("Current: %c\n", current -> value);
for (int i = 0; i < current -> edge_count; i++) {
Node* neighbor = current -> edges[i];
if (!neighbor -> visited) {
printf("New neighbor: %c\n", neighbor -> value);
const int tentative_distance = current -> distance + current -> weights[i];
if (tentative_distance < neighbor -> distance)
neighbor -> distance = tentative_distance;
if (neighbor -> distance < closest -> distance)
closest = neighbor;
}
}
printf("%c, closest, distance of %d\n", closest -> value, closest -> distance);
current -> visited = 1;
if (closest == goal) printf("Shortest distance is %d\n", closest -> distance);
else dijkstra(closest, goal);
}
int main() {
Node
*a = init_node('a', 2),
*b = init_node('b', 3),
*c = init_node('c', 3),
*d = init_node('d', 2),
*e = init_node('e', 2),
*f = init_node('f', 2),
*g = init_node('g', 2),
*h = init_node('h', 2);
assign_edges(a, 2, e, b);
assign_edges(b, 3, a, f, c);
assign_edges(c, 3, b, h, d);
assign_edges(d, 2, c, g);
assign_edges(e, 2, a, g);
assign_edges(f, 2, b, h);
assign_edges(g, 2, e, d);
assign_edges(h, 2, f, c);
assign_weight(a, e, 2);
assign_weight(a, b, 4);
assign_weight(b, c, 1);
assign_weight(b, f, 1);
assign_weight(f, h, 1);
assign_weight(h, c, 1);
assign_weight(c, d, 2);
assign_weight(d, g, 1);
assign_weight(g, e, 1);
e -> distance = 0;
dijkstra(e, f);
deinit_graph(a);
}
【问题讨论】:
-
考虑:如果没有有效的最近节点,是否应该递归?在这种情况下,当你递归时会发生什么?
-
@1201ProgramAlarm 当然;如果您没有有效的最近节点,则重复出现是没有意义的。如果我说
if (closest -> distance == infinity) return;,我的实现在找到解决方案之前就停止了,这是不正确的。我的假设是否正确,总会有一个有效的最近节点?我还没有找到一个例子来说明如何处理没有节点有效的情况。
标签: c graph shortest-path dijkstra graph-traversal