【发布时间】:2019-07-05 10:43:04
【问题描述】:
我目前正在开发一个必须解决 10x10 字符迷宫的程序,例如这个:
1 2 3 4 5 6 7 8 9 10
___________________________________________
1| + [] + [] + [] + + + []
2| + [] + + [] + + [] [] []
3| + + [] [] [] + [] [] + +
4| [] [] [] [] + + + [] [] +
5| + [] + [] + [] + + + []
6| + + + + + + [] [] + []
7| [] + + + [] [] + + + []
8| + + + [] + + + [] [] +
9| + + + [] [] + + [] [] +
10| + [] + [] + + [] + + +
忽略数字,它们只是坐标。至于[],这只是迷宫的打印方式。实际上,只要有+,就意味着路径,无论有[],就意味着有障碍。
我正在使用回溯算法:
void backtrack(int curX, int curY, char(*char_maze)[10], int position)
{
if (curX < 0 || curY < 0 ||
curX > 9 || curY > 9) {
//out of bounds
return;
}
Node tmp;
tmp.x = curX, tmp.y = curY;
queue(&head, &tmp);
position++;
if (char_maze[curX][curY] == finish) {
//destination found TODO print path
printf("route found");
}
if (char_maze[curX][curY] == path) {
char_maze[curX][curY] = visited;
}
backtrack(curX, curY - 1, char_maze, position);
backtrack(curX - 1, curY, char_maze, position);
backtrack(curX, curY + 1, char_maze, position);
backtrack(curX + 1, curY, char_maze, position);
char_maze[curX][curY] = path;
if (position) {
del_nth(head, position);
}
if (!position) {
del_first(&head);
}
position--;
}
正确的路线将由一个链表组成,这里是该链表的一个节点:
typedef struct coords {
int x;
int y;
struct coords * next;
}Node;
每当backtrack(...) 偶然发现一个可通过的单元格时,它应该将其标记为已访问并将其添加到列表中。添加到列表是由这两个函数完成的:
void queue(Node ** head, Node * object)
{
Node * tmp = (Node *)malloc(sizeof(Node)); //this is the problematic line
*tmp = *object;
Node * last = get_last(*(head));
if (!last) {
(*head) = tmp;
tmp->next = NULL;
}
else {
last->next = tmp;
tmp->next = NULL;
}
}
和
Node * get_last(Node * head)
{
while (1) {
if (head) {
head = head->next;
}
else {
return NULL;
}
}
return head;
}
并且在适当的条件下backtrack(...) 应该取消标记一个单元格并将其从列表中删除。删除是使用这两个函数完成的:
void del_nth(Node * head, int index)
{
Node * previous;
for (int i = 0; i < index - 1; i++) {
head = head->next;
}
previous = head;
head = head->next;
previous->next = head->next;
free(head);
}
和
void del_first(Node ** head)
{
Node * del = (*head);
(*head) = (*head)->next;
free(del);
}
path, visited, finish等是const char-s,代表迷宫的单元格。
backtrack(...)
使用用户设置的 2 个坐标调用,迷宫本身和设置为 0 的 position。
现在我解释了代码是如何工作的,问题就来了。我已经通过 Visual Studio 调试器运行了这段代码,我在这一行得到了一个 Stack overflow (parameters: 0x00000001, 0x00492FFC). 异常
Node * tmp = (Node *) malloc(sizeof(Node));
这是queue(...) 函数的一部分。这对我来说没有任何意义,因为malloc() 在堆上分配。我被难住了,我没有解释,我不知道为什么会发生这种情况。我包含了backtrack(...) 函数中使用的所有代码,因为问题可能确实存在。这不是调试器第一次指出错误的行。
无论如何,非常感谢您提前提供的帮助。
【问题讨论】:
-
如果一个递归函数给你一个堆栈溢出,那是因为每次递归调用都会创建堆栈帧;堆分配与它无关。
-
您可以将
get_last替换为return NULL,因为它就是这样做的。 -
即使已经访问了单元格,您仍然会调用回溯。你应该只在它仍然是路径时调用。
-
递归中没有基本情况。只有当你越界时你才停止递归,但没有什么能阻止你在两个单元格之间来回走动。
-
当单元格已经被访问过时,需要立即返回。
标签: c recursion heap-memory backtracking