給定一個二維網格迷宮,1 代表可通行路徑,0 代表牆壁,2 代表終點。從起點 (1,0) 出發,只能上下左右四向移動,尋找一條到達終點的路徑(或列舉所有路徑)。
本質是對「狀態空間樹」的前序遍歷:
(x,y),若合法且未訪問,標記為已訪問。
#define MAX_N 100
int maze[MAX_N][MAX_N]; // 原始迷宮:0=牆, 1=路, 2=終點
int visited[MAX_N][MAX_N]; // 0=未訪問, 1=路徑上, 2=已嘗試失敗(可選)
int path_x[MAX_N*MAX_N]; // 路徑座標記錄
int path_y[MAX_N*MAX_N];
int path_len = 0;
bool is_valid(int x, int y, int n) {
return x >= 0 && x < n && y >= 0 && y < n && // 邊界
maze[x][y] != 0 && // 非牆壁
visited[x][y] == 0; // 未訪問
}
bool dfs_find_one(int x, int y, int n) {
if (!is_valid(x, y, n)) return false;
visited[x][y] = 1;
path_x[path_len] = x;
path_y[path_len] = y;
path_len++;
if (maze[x][y] == 2) return true; // 找到終點
// 四方向順序可影響找到的具體路徑
if (dfs_find_one(x-1, y, n)) return true; // 上
if (dfs_find_one(x, y-1, n)) return true; // 左
if (dfs_find_one(x+1, y, n)) return true; // 下
if (dfs_find_one(x, y+1, n)) return true; // 右
// 回溯
path_len--;
visited[x][y] = 0;
return false;
}
void dfs_find_all(int x, int y, int n) {
if (!is_valid(x, y, n)) return;
visited[x][y] = 1;
path_x[path_len] = x;
path_y[path_len] = y;
path_len++;
if (maze[x][y] == 2) {
print_path(); // 列印當前路徑
} else {
dfs_find_all(x-1, y, n);
dfs_find_all(x, y-1, n);
dfs_find_all(x+1, y, n);
dfs_find_all(x, y+1, n);
}
path_len--;
visited[x][y] = 0;
}
void print_path(void) {
for (int i = 0; i < path_len; ++i) {
printf("(%d,%d) ", path_x[i], path_y[i]);
}
putchar('\n');
}
void test_maze(void) {
int n = 6;
int init[6][6] = {
{0,0,0,0,1,1},
{1,1,0,0,1,0},
{0,1,1,1,1,0},
{0,0,1,0,1,2},
{0,0,1,0,1,0},
{0,0,1,1,1,0}
};
memcpy(maze, init, sizeof(init));
memset(visited, 0, sizeof(visited));
path_len = 0;
if (dfs_find_one(1, 0, n)) {
printf("Found path:\n");
print_path();
} else {
puts("No path");
}
// 列舉所有路徑
memset(visited, 0, sizeof(visited));
path_len = 0;
printf("\nAll paths:\n");
dfs_find_all(1, 0, n);
}
| 指標 | 數值 | 說明 |
|---|---|---|
| 時間複雜度 | O(4n²) | 最壞情況(全通路網格),每個格嘗試 4 方向 |
| 空間複雜度 | O(n²) | visited 陣列 + 遞迴棧深度 + 路徑記錄 |
| 最優路徑 | 不保證 | DFS 找到的第一解非最短路徑 |
f = g + h,保證找到最短路徑,工程首選。visited 用 uint64_t 位圖,加速複製與檢查(適合 n ≤ 8)。| 錯誤 | 後果 | 修正 |
|---|---|---|
忘記回溯時清除 visited | 後續路徑誤判為已訪問,漏解 | 退出遞迴前 visited[x][y]=0 |
| 邊界檢查順序錯誤 | 先存取 maze[x][y] 再檢查邊界 → 段錯誤 | 先檢查 x,y 範圍 |
| 起點/終點本身為牆壁 | 直接返回無解 | 進入 DFS 前驗證 maze[sx][sy]!=0 && maze[tx][ty]!=0 |
| 路徑記錄陣列溢出 | 緩衝區溢出 | 分配 n*n 大小或動態擴展 |
回溯法是解決「約束滿足、路徑搜尋、組合生成」問題的通用模板。迷宮尋路通過「標記 → 遞迴 → 回溯」三步曲,直觀展示了 DFS 對狀態空間的完整遍歷。工程上:單解用 DFS+啟發式,最短路徑用 BFS/A*,全路徑枚舉用 DFS+回溯。掌握此模板,可無縫遷移至 N 皇后、數獨求解、圖論 Hamiltonian 路徑等經典問題。