迷宫寻路是回溯算法的经典应用场景,其核心是在复杂路径中通过 "尝试 - 回溯" 探索所有可能的路径,最终找到通往终点的解。无论是在算法竞赛(如 LeetCode)还是考研计算机专业基础综合(408)中,迷宫寻路问题及其变种都是考察回溯思想的高频考点。
迷宫寻路的回溯算法思路
问题定义
迷宫通常被抽象为一个二维网格,其中:
- 0 表示可通行的空地。
 
- 1 表示不可通行的墙壁。
 
- 起点 (startX, startY) 和终点 (endX, endY) 是网格中的两个特殊位置。
 
问题目标:从起点出发,通过上下左右四个方向移动,找到一条到达终点的路径(或所有路径),移动过程中不能穿过墙壁,也不能重复经过同一位置。
回溯算法核心思路
回溯算法通过递归探索所有可能的路径,若当前路径无法到达终点,则回溯到上一步,尝试其他方向。具体步骤如下:
- 标记当前位置:到达某位置 (x, y) 后,标记该位置为 "已访问"(避免重复进入)。
 - 判断终点:若 (x, y) 是终点,则记录当前路径(若需所有路径)或直接返回成功。
 - 尝试方向:依次尝试上、下、左、右四个方向:
 
- 若方向未越界、不是墙壁、未被访问,则递归探索该方向。
 
- 若递归返回成功(找到终点),则继续回溯或返回;若失败,则尝试下一个方向。
 - 回溯清理:若所有方向均无法到达终点,取消当前位置的 "已访问" 标记,返回上一步。
 
算法流程图示
(以 3×3 迷宫为例)

LeetCode例题实战
例题1:490. 迷宫(中等)
题目描述
:由空地(0)和墙(1)组成的迷宫中有一个球。球可以向上下左右四个方向滚动,但在遇到墙之前不会停止滚动。当球停下时,可以选择下一个方向。给定球的起始位置、目的地和迷宫,判断球能否在目的地停下。
示例:
输入:
迷宫:[[0,0,1,0,0],[0,0,0,0,0],[0,0,0,1,0],[1,1,0,1,1],[0,0,0,0,0]]
起始位置:[0,4]
目的地:[4,4]
输出:true
解释:球可以滚动到目的地(路径:右→下→左→下→右)
解题思路
1. **与传统迷宫的区别**:球会"滚动"直到撞墙才停下,而非一步一格移动。
2. **回溯思路**:
- 从起点出发,尝试四个方向滚动,记录停下的位置(撞墙前的最后一个空地)。
- 若停下的位置是终点,返回true。
- 若该位置已访问过,跳过(避免循环)。
- 否则标记为已访问,递归探索从该位置出发的四个方向。
- 回溯时无需取消标记(因滚动路径唯一,不会重复访问)。
代码实现
        
            
            
              java
              
              
            
          
          class Solution {
    private int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}}; // 上、下、左、右
    private int rows, cols;
    private boolean[][] visited;
    public boolean hasPath(int[][] maze, int[] start, int[] destination) {
        rows = maze.length;
        cols = maze[0].length;
        visited = new boolean[rows][cols];
        return backtrack(maze, start[0], start[1], destination);
    }
    private boolean backtrack(int[][] maze, int x, int y, int[] dest) {
// 若当前位置是终点,返回true
        if (x == dest[0] && y == dest[1]) {
            return true;
        }
// 标记当前位置为已访问
        visited[x][y] = true;
// 尝试四个方向滚动
        for (int[] dir : directions) {
            int nx = x, ny = y;
// 滚动直到撞墙
            while (nx + dir[0] >= 0 && nx + dir[0] < rows &&
                    ny + dir[1] >= 0 && ny + dir[1] < cols &&
                    maze[nx + dir[0]][ny + dir[1]] == 0) {
                nx += dir[0];
                ny += dir[1];
            }
// 若停下的位置未访问,递归探索
            if (!visited[nx][ny] && backtrack(maze, nx, ny, dest)) {
                return true;
            }
        }
// 所有方向均无法到达,返回false
        return false;
    }
}
        复杂度分析
- 时间复杂度:O (rows×cols×(rows+cols)),每个位置最多访问一次,每次滚动最多移动 rows+cols 格。
 
- 空间复杂度:O (rows×cols),visited 数组和递归栈的空间。
 
例题 2:79. 单词搜索(中等)
题目描述:给定一个 m x n 二维网格和一个单词,找出该单词是否存在于网格中。单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中 "相邻" 单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
输入:board = [["A","B","C","E"],["S","F","C","S"],["A","D","E","E"]], word = "ABCCED"
输出:true
解题思路
- 问题抽象:网格可视为特殊迷宫,每个单元格是 "字母",路径需按单词顺序,且不可重复访问。
 - 回溯思路:
 
- 遍历网格,找到单词首字母作为起点。
 
- 从起点出发,尝试四个方向,若下一个字母匹配且未访问,递归探索。
 
- 若递归长度等于单词长度,返回 true。
 
- 回溯时取消当前单元格的访问标记。
 
代码实现
            
            
              java
              
              
            
          
          class Solution {
    private int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    private int rows, cols;
    private boolean[][] visited;
    public boolean exist(char[][] board, String word) {
        rows = board.length;
        cols = board[0].length;
        visited = new boolean[rows][cols];
// 遍历网格找起点
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols; j++) {
                if (board[i][j] == word.charAt(0) && backtrack(board, word, i, j, 0)) {
                    return true;
                }
            }
        }
        return false;
    }
    private boolean backtrack(char[][] board, String word, int x, int y, int index) {
// 若匹配到单词末尾,返回true
        if (index == word.length() - 1) {
            return true;
        }
        visited[x][y] = true;
// 尝试四个方向
        for (int[] dir : directions) {
            int nx = x + dir[0];
            int ny = y + dir[1];
// 检查边界、未访问、字母匹配
            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols &&
                    !visited[nx][ny] && board[nx][ny] == word.charAt(index + 1)) {
                if (backtrack(board, word, nx, ny, index + 1)) {
                    return true;
                }
            }
        }
// 回溯:取消标记
        visited[x][y] = false;
        return false;
    }
}
        复杂度分析
- 时间复杂度:O (m×n×3^L),m 和 n 为网格尺寸,L 为单词长度。每个单元格最多访问一次,每次有 3 个新方向(排除来时方向)。
 
- 空间复杂度:O (L),递归栈深度为 L(单词长度),visited 数组为 O (m×n)。
 
考研 408 例题解析
例题 1:概念辨析题(选择题)
题目:关于回溯算法求解迷宫寻路问题,下列说法正确的是( )。
A. 回溯算法总能找到最短路径
B. 回溯算法的时间复杂度一定优于广度优先搜索(BFS)
C. 回溯算法通过递归实现,无需显式栈
D. 回溯过程中必须恢复访问标记,否则可能遗漏有效路径
答案:D
解析:
- A 错误:回溯算法会探索所有路径,但不一定优先找到最短路径(BFS 更适合最短路径)。
 
- B 错误:回溯算法时间复杂度通常为指数级(如 O (4^(m×n))),而 BFS 为 O (m×n),更优。
 
- C 错误:回溯算法的递归本质是利用系统栈,仍属于栈结构,只是无需手动实现。
 
- D 正确:若不恢复访问标记,已探索路径的标记会阻塞其他可能的有效路径,导致漏解。
 
例题 2:算法设计题(408 高频考点)
题目:设计一个回溯算法,找出迷宫中从起点到终点的所有路径。要求每条路径用坐标序列表示(如(0,0)→(0,1)→...→(m-1,n-1)),并分析算法的时间复杂度。
解题思路
- 数据结构:
 
- 
- maze:二维数组表示迷宫(0 为通路,1 为墙)。
 
 
- 
- path:列表记录当前路径的坐标。
 
 
- 
- visited:二维数组标记已访问的位置。
 
 
- 
- result:列表存储所有有效路径。
 
 
- 递归函数:backtrack(x, y)表示从(x,y)出发探索路径。
 - 步骤:
 
- 
- 若(x,y)是终点,将当前path加入result。
 
 - 
- 否则,对四个方向:
 
 
- 
- 
- 若方向合法(不越界、非墙、未访问),标记访问,加入path,递归探索。
 
 
 - 
 
- 
- 
- 回溯:移除path中的坐标,取消访问标记。
 
 
 - 
 
代码实现
            
            
              java
              
              
            
          
          import java.util.*;
public class MazeAllPaths {
    private int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
    private int rows, cols;
    private List<List<String>> result = new ArrayList<>();
    public List<List<String>> findAllPaths(int[][] maze, int[] start, int[] end) {
        rows = maze.length;
        cols = maze[0].length;
        boolean[][] visited = new boolean[rows][cols];
        List<String> path = new ArrayList<>();
// 起点合法性检查
        if (maze[start[0]][start[1]] == 1) {
            return result;
        }
        path.add("(" + start[0] + "," + start[1] + ")");
        visited[start[0]][start[1]] = true;
        backtrack(maze, start[0], start[1], end, visited, path);
        return result;
    }
    private void backtrack(int[][] maze, int x, int y, int[] end,
                           boolean[][] visited, List<String> path) {
// 到达终点,记录路径
        if (x == end[0] && y == end[1]) {
            result.add(new ArrayList<>(path));
            return;
        }
// 尝试四个方向
        for (int[] dir : directions) {
            int nx = x + dir[0];
            int ny = y + dir[1];
// 检查边界、是否为墙、是否已访问
            if (nx >= 0 && nx < rows && ny >= 0 && ny < cols &&
                    maze[nx][ny] == 0 && !visited[nx][ny]) {
// 标记访问,加入路径
                visited[nx][ny] = true;
                path.add("(" + nx + "," + ny + ")");
// 递归探索
                backtrack(maze, nx, ny, end, visited, path);
// 回溯:移除路径,取消标记
                path.remove(path.size() - 1);
                visited[nx][ny] = false;
            }
        }
    }
}
        复杂度分析
- 时间复杂度:O (4^(m×n)),每个单元格最多被访问一次,每次有 4 个方向可选,最坏情况下需探索所有可能路径。
 
- 空间复杂度:O (m×n),包括visited数组(m×n)、递归栈深度(最坏为 m×n)和路径存储(最长路径长度为 m×n)。
 
迷宫寻路的扩展与应用
实际应用场景
- 机器人导航:机器人在未知环境中探索路径,类似迷宫寻路逻辑。
 
- 游戏开发:角色在地图中寻找宝藏、避开障碍物的路径规划。
 
- 网络路由:数据包在网络节点间的传输路径选择,需避开故障节点。
 
与其他算法的对比
|-------------|-------------|---------------|------------------|
| 算法          | 核心思路        | 适用场景          | 时间复杂度            |
| 回溯算法        | 递归探索所有路径    | 求所有路径、判断路径存在性 | O(4^(m×n))      |
| 广度优先搜索(BFS) | 队列逐层探索      | 求最短路径         | O(m×n)           |
| 深度优先搜索(DFS) | 栈或递归深度探索    | 与回溯类似,实现方式不同  | O(4^(m×n))      |
| A * 算法     | 启发式搜索(估价函数) | 大规模迷宫的高效路径规划  | 接近 O (m×n)(最优情况) |
考研 408 备考要点
- 核心考点:回溯算法的递归实现、访问标记的管理、路径存储与恢复。
 
- 重点掌握:
 
- 迷宫寻路中 "边界检查""墙检查""访问检查" 三要素。
 - 回溯与递归的关系,以及回溯过程中状态的恢复逻辑。
 - 不同迷宫变种(如滚动球、带权值迷宫)的算法调整。
 
- 常见错误:
 - 
- 遗漏边界检查导致数组越界。
 
 
- 
- 回溯时未恢复访问标记,导致路径探索不完整。
 
 
- 
- 混淆 "一步一格" 与 "滚动到墙" 的移动逻辑。
 
 
总结
迷宫寻路问题是回溯算法的典型应用,其核心 "递归探索 - 标记 - 回溯" 逻辑可迁移到多种路径规划场景。本文通过 LeetCode 例题(490 题和 79 题)展示了不同约束条件下的回溯实现,通过考研 408 例题解析了概念辨析和全路径求解思路,并结合 SVG 图示直观呈现了探索与回溯的过程。
掌握迷宫寻路的关键在于:
- 明确问题约束(移动方式、路径规则),针对性设计探索逻辑。
 - 严格管理访问标记,确保回溯时状态正确恢复。
 - 理解回溯算法与其他路径算法的适用场景差异(如 BFS 更适合最短路径)。
 
在考研备考中,需重点关注回溯算法的时间复杂度分析和状态管理细节,这不仅有助于解决迷宫问题,也能深化对递归和组合优化问题的理解。
希望本文能够帮助读者更深入地理解贪心算法中迷宫寻路算法,并在实际项目中发挥其优势。谢谢阅读!
希望这份博客能够帮助到你。如果有其他需要修改或添加的地方,请随时告诉我。
