【问题标题】:Backtrack algorithm, Stack overflow error, despite heap allocation回溯算法,堆栈溢出错误,尽管堆分配
【发布时间】: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 个坐标调用,迷宫本身和设置为 0position

现在我解释了代码是如何工作的,问题就来了。我已经通过 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


【解决方案1】:

你的函数应该返回它找到的路径。如果当前位置不是开放空间,它也应该立即返回。只要找到解决方案,它也应该停止。

提示:在大多数递归算法中,返回值用于停止递归,因此递归函数通常会返回一些感兴趣的东西,比如运算结果。

例子:

Node* backtrack(int curX, int curY, char(*char_maze)[10], int position)
{
    Node* node = NULL;

    // return as fast as possible for
    if (curX < 0 || curY < 0 || curX > 9 || curY > 9) 
    {
        return NULL;
    }
    if (position > 100)  // 10 * 10 cells.
    {
        // should not reach this point.
        printf("iteration depth exceeded maximum limit!!\n")
        return NULL;
    }

    if (char_maze[curX][curY] == finish) 
    {
        printf("route found");
        node = malloc(sizeof(Node));
        if (node)
        {
            node->x = curX;
            node->y = curY;
            node->next = NULL;
        }
        else
        {
            printf("*** ERROR *** malloc returns NULL (1)!!!\n");
        }

        return node;  // this return point is what triggers the backtrack chaining
                      // of positions
    }
    if (char_maze[curX][curY] != path) // nothing to do
    {
        return NULL;
    }

    // mark current location as being worked on.
    char_maze[curX][curY] = visited;

    // now try possible moves, stop looking as soon as a path is found.
    if ((node = backtrack(curX, curY - 1, char_maze, position + 1) != NULL
        || (node = backtrack(curX - 1, curY, char_maze, position + 1) != NULL
        || (node = backtrack(curX, curY + 1, char_maze, position + 1) != NULL
        || (node = backtrack(curX + 1, curY, char_maze, position + 1) != NULL)
    {
        // path found, add current position to the head of the list
        Node* p = malloc(sizeof(Node));
        if (p)
        {
            p->x = curX;
            p->y = curY;
            p->next = node;
            node = p;
        }
        else
        {
            // out of memory, free whatever was allocated before returning.
            printf("*** ERROR *** malloc returns NULL (2)!!!\n");
            while (node)
            {
                p = node->next;
                free(node);
                node = p;
            }
        }
    }

    char_maze[curX][curY] = path;  // clean up.

    return node; // return path found (or not found);
}

注意:我没有编译这段代码,但这应该可以帮助你完成你的程序。

【讨论】:

    猜你喜欢
    • 2019-02-02
    • 2011-05-16
    • 1970-01-01
    • 2011-10-30
    • 2014-01-26
    • 2019-02-16
    • 2011-09-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多