Bootstrap

(8-4)优先级遍历(Priority-based Search)算法:深度优先搜索(Depth First Search)算法

8.4  深度优先搜索(Depth First Search)算法

深度优先搜索(Depth First Search,DFS)是一种用于遍历或搜索树或图的算法,它从起始节点开始沿着一条路径一直向前直到达到末端,然后回溯到上一个节点,继续沿着另一条路径探索,直到所有节点都被访问过为止。DFS 使用栈(Stack)来存储待访问的节点。

8.4.1  深度优先搜索算法介绍

深度优先搜索(Depth First Search,DFS)是一种经典的图遍历算法,用于在图中搜索或遍历节点。DFS属于一种盲目搜索算法,没有启发式信息的辅助,而是通过深度优先的方式探索图中的节点。

1. 基本思想

  1. 从起始节点开始,访问一个节点后立即探索它的邻居节点,深入到最深的节点,直到到达最末端。
  2. 当无法再继续深入时(即当前节点没有未访问的邻居节点),回溯到上一个节点,继续深度优先搜索其他分支。
  3. 重复这个过程,直到遍历到所有节点或者达到搜索终止条件。

2. 实现方式

  1. 递归实现:DFS可以使用递归来实现,利用递归函数不断调用自身来实现深度优先搜索。
  2. 栈实现:DFS可以使用栈来实现,利用栈数据结构来模拟递归过程,手动维护遍历的状态信息。

下面是一个简单的深度优先搜索算法的伪代码实现:

procedure DFS(G, v):
    stack S := empty stack
    S.push(v)
    while S is not empty:
        current := S.pop()
        if current is not visited:
            visit(current)
            mark current as visited
            for each neighbor w of current in G:
                if w is not visited:
                    S.push(w)

在上述伪代码中,G 表示图,v 表示起始节点。在 DFS 过程中,首先创建一个空栈 S,然后将起始节点 v 入栈。然后,循环直到栈为空,从栈中弹出一个节点 current。如果 current 节点尚未被访问过,则对其进行访问并标记为已访问。然后,遍历 current 节点的所有未访问的相邻节点,并将它们入栈。最终,重复这个过程直到栈为空,所有节点都被访问过。

深度优先搜索算法的时间复杂度是 O(V + E),其中 V 是节点的数量,E 是边的数量。

8.4.2  寻找猫咪的最短路径

下面是一个使用深度优先搜索算法为猫咪寻找最短路径的例子,在这个例子中,使用一个二维列表来表示迷宫,其中0表示可通行的空格,1表示障碍物。假设在一个迷宫中,有一只可爱的猫咪迷失了方向,它想要从起始点(左上角)尽快找到出口(右下角),你能帮助它找到最短路径吗?

迷宫是一个由0和1组成的矩阵,其中0表示可通行的空格,1表示墙壁阻挡。猫咪只能水平或垂直移动,不能斜向移动,并且它不能穿过墙壁。请使用深度优先搜索算法来帮助猫咪找到最短路径,并将路径在迷宫中以红色线条标识出来。下面是迷宫示例(0表示可通行,1表示墙壁):

maze = [
    [0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0],
    [1, 1, 0, 1, 0],
    [1, 1, 0, 0, 0],
    [1, 1, 1, 1, 0]
]

实例8-5使用深度优先搜索算法为猫咪寻找最短路径codes/8/ddfs.py

实例文件ddfs.py的具体实现代码如下所示。

import matplotlib.pyplot as plt

def is_valid_move(maze, row, col, visited):
    rows = len(maze)
    cols = len(maze[0])
    return row >= 0 and row < rows and col >= 0 and col < cols and maze[row][col] == 0 and not visited[row][col]

def dfs_shortest_path(maze, row, col, destination, visited, path, shortest_path):
    if row == destination[0] and col == destination[1]:
        shortest_path[:] = path
        return True
    
    directions = [(0, 1), (1, 0), (0, -1), (-1, 0)]  # 右、下、左、上

    for dr, dc in directions:
        new_row = row + dr
        new_col = col + dc
        if is_valid_move(maze, new_row, new_col, visited):
            visited[new_row][new_col] = True
            path.append((new_row, new_col))
            if dfs_shortest_path(maze, new_row, new_col, destination, visited, path, shortest_path):
                return True
            visited[new_row][new_col] = False
            path.pop()

    return False

def find_shortest_path(maze):
    rows = len(maze)
    cols = len(maze[0])

    start = (0, 0)
    destination = (rows - 1, cols - 1)
    visited = [[False for _ in range(cols)] for _ in range(rows)]
    visited[0][0] = True

    shortest_path = []
    dfs_shortest_path(maze, 0, 0, destination, visited, [(0, 0)], shortest_path)
    return shortest_path

def plot_maze_with_path(maze, shortest_path):
    rows = len(maze)
    cols = len(maze[0])

    plt.figure(figsize=(cols, rows))
    plt.imshow(maze, cmap='binary')

    for i in range(len(shortest_path) - 1):
        current_row, current_col = shortest_path[i]
        next_row, next_col = shortest_path[i+1]
        plt.plot([current_col, next_col], [current_row, next_row], color='red', linewidth=2)

    plt.xticks(range(cols))
    plt.yticks(range(rows))
    plt.gca().invert_yaxis()  # 反转y轴,使得坐标原点在左上角
    plt.grid(color='gray', linestyle='--', linewidth=0.5)
    plt.title('Maze with Shortest Path')
    plt.show()

# 示例迷宫
maze = [
    [0, 1, 0, 0, 0],
    [0, 0, 0, 1, 0],
    [1, 1, 0, 1, 0],
    [1, 1, 0, 0, 0],
    [1, 1, 1, 1, 0]
]

shortest_path = find_shortest_path(maze)
if shortest_path:
    print("Shortest path found:")
    for row, col in shortest_path:
        print(f"({row}, {col}) -> ", end="")
    print("Destination")
    
    plot_maze_with_path(maze, shortest_path)
else:
    print("No path found!")

上述代码的实现流程如下所示:

(1)首先,定义了一个迷宫,迷宫是一个二维列表,其中0表示可通行的空格,1表示墙壁阻挡。这个迷宫是一个5x5的矩阵,代表了猫咪可能迷失的区域。

(2)然后,使用深度优先搜索算法来帮助猫咪找到从起始点(左下角)到出口(右上角)的最短路径。首先定义了函数 is_valid_move 来检查机器人是否可以朝某个方向移动,然后使用深度优先搜索来找到最短路径。

(3)接着,定义函数plot_maze_with_path,用于在找到的最短路径上绘制了红色线条,以便清晰地看到猫咪应该走的路径。通过使用 matplotlib 库来绘制迷宫和最短路径,将最短路径以红色线条标识出来。

(4)最后,在主程序中调用相应的函数,传入迷宫信息,找到最短路径并将其绘制出来。这样,就完成了寻找猫咪最短路径的任务。

执行后会打印输出如下所示的路径信息,并绘制如图8-4所示的路径可视化图。

Shortest path found:
(0, 0) -> (1, 0) -> (1, 1) -> (1, 2) -> (2, 2) -> (3, 2) -> (3, 3) -> (3, 4) -> (4, 4) -> Destination

图8-4  路径可视化图

;