使用递归确定路径

时间:2016-05-21 00:29:09

标签: c

我试图找到迷宫中的最短路径,到目前为止我可以打印这个

##########00######00##########
##++04++02++##++##++02++04++##
##06######02##14##########06##
##++##++04++##++12++10++08++##
##08++06##################08##
######++##++14++14++12++10++##
##12##08##12##################
##++10++10++##--------------##
##12##########--##########--##
##++14++16++##--------------##
##########18##################

数字代表整个路径的成本。从顶部的入口点开始,这个迷宫中有多条路径,在迷宫中可以看到很多路径。我希望找到最短的路径,看起来像这样:

##########00######  ##########
##        ..##  ##          ##
##  ######02##  ##########  ##
##  ##..04..##              ##
##    06##################  ##
######..##                  ##
##  ##08##  ##################
##..10..    ##--------------##
##12##########--##########--##
##..14..16..##--------------##
##########18##################

我使用递归来查找这些费用,我只是在寻找关于如何实现这一目标的建议。成本值存储在我的程序中,所以我只想找到一种方法来解决迷宫中的最短路径。

有没有办法可以调整此功能以找到最短路径? 任何帮助,将不胜感激。

2 个答案:

答案 0 :(得分:2)

  

有没有办法可以调整此函数以找到最短路径?

是。更新maze_t结构以使成员指向前任。每当traverse()更新节点的min_cost时,让它还将该节点的前任设置为以该成本可以从该节点到达的节点的前一个节点。完成该功能后,您可以通过前任链追踪从出口到入口的最小长度路径。 (可以有多个最小长度路径;这种方法只能获得其中一个。)

然而,请注意,人们通常会使用Dijkstra算法或类似的最优先搜索算法来进行最短路径确定。 更新:由于每个路径中的每个步骤都具有相同的增量成本,因此@TomKarzes建议的普通广度优先搜索也可以作为最佳优先搜索。您的深度优先方法效率非常低比较。

答案 1 :(得分:2)

首先对您当前的代码进行一些评论:

reachable属性的类型

maze->MAZE[row][col].reachable = "+";

这表明reachable是一个字符串,但这有点矫枉过正。将其定义为char,然后写:

maze->MAZE[row][col].reachable = '+';

入口点也是出口

在示例迷宫中,您将起点设置为(0, 5),但如果目标是找到退出迷宫的最短路径,那么当然只有一步之遥:上去,然后你出去了。这肯定不是你想要的,所以为了给出正确的最短路径,你需要关闭那个出口。我建议使用这个修改过的示例迷宫,其中起点将移至(1, 5)。我用*

标记了它
    #########.#####
    #....*#.#.....#
    #.###.#.#####.#
    #.#...#.......#
    #...#########.#
    ###.#.........#
    #.#.#.#########
    #.....#.......#
    #.#####.#####.#
    #.....#.......#
    #####.#########

现在已经没有这样的捷径了。

不必要的if条件

reachable上的四个条件是不必要的:

由于您刚刚将reachable属性设置为+,因此将始终满足后面的if语句,因此不需要这些条件。

还有第二个条件:

maze->MAZE[row][col].type != '.'

...没有必要,因为你已经在第一次真实时退出该函数。所以第二个if可以不用这个。

在循环中放入递归调用

您可以将它放在for循环中,而不是在代码中重复四次递归调用,而是使用一个给出方向的数组:

int dir, direction[] = {0, -1, 0, 1, 0};
// ...
for (dir = 0; dir < 4; dir++) {
    traverse(maze, row + direction[dir], col + direction[dir+1], cost + 1);
}

添加什么以获得最短路径

现在基于上面调整的代码,您可以计算逆成本,即到出口的最短距离。这可以作为traverse函数的值返回。最重要的是,你可以将每个单元格的方向存储为0到3之间的值。我已经调用了这个新属性shortest_path_direction

函数traverse将如下所示:

int direction[] = {0, -1, 0, 1, 0};

int traverse(maze_t * maze, int row, int col, int cost) {
    int dir, dist, best_dist = 1000000; // represents "infinity"

    if ( row < 0 || row >= maze->rows || col < 0 || col >= maze->columns ) {
        return 0; // distance from exit is 0.
    }
    // If row,col is not open
    if (maze->MAZE[row][col].type != '.') {
        return best_dist; // not possible, distance is infinite 
    }
    // Compare costs of different paths
    if (maze->MAZE[row][col].min_cost < cost) {
        return best_dist; // not shortest path, distance is infinite
    }
    maze->MAZE[row][col].min_cost = cost;
    // Mark row,col as part of path.
    maze->MAZE[row][col].reachable = '+'; // use char type
    // Search neighboring cells
    for (dir = 0; dir < 4; dir++) {
        dist = traverse(maze, row + direction[dir], col + direction[dir+1], 
                              cost + 1);
        if (dist < best_dist) {
            best_dist = dist;
            maze->MAZE[row][col].shortest_path_direction = dir;
        }
    }
    return best_dist+1;
}

您可以看到代码在eval.in上运行,其中我添加了一个从字符串加载迷宫的函数,以及根据新属性{{将最短路径打印为一系列坐标的函数1}}。

性能

这不是你的问题,但你应该知道你所做的搜索不是最佳的。在示例迷宫中,您首先找到如下所示的路径:

shortest_path_direction

然后是另一条道路:

    ######### #####
    #.....# #     #
    #.### # ##### #
    #.#   #       #
    #...######### #
    ###.#         #
    # #.# #########
    #...  #       #
    #.##### ##### #
    #.....#       #
    #####.#########

之前已经访问了大部分路径(包括所有非最佳分支),这是浪费时间。一种有效的算法不会再次通过整个分支。

虽然可以改进,但你应该看一下广度优先搜索。