老鼠走迷宮這類題目,就資料結構這門學科要探討的範圍來說,是要用程式模擬老鼠在迷宮中尋找出路的過程。這類問題在實務上也有許多應用,例如在電路設計中,就是用走迷宮的概念,來找出兩個元件間的佈線路徑。本篇的目標,則是探討如何用 stack 來找到通往迷宮出口的路。

使用 stack 來找迷宮出口的方法也非常簡單,就是從起點開始,往固定的方向一直試,撞牆了再依照既定順序換個方向繼續試,所有方向都走不通了就巴庫(倒退)回去,回到還有方向沒試過的地方就再繼續試,直到找到出口為止,或者當所有方向都試過了卻還沒找到出口,就是沒有路徑從起點通往出口。這個方法因為需要反覆地前進倒退,所以很自然地會跟 stack 產生關聯:前進的時候就把路徑 push 進 stack,需要後退的時候就使用 pop。

上述方法的實作如下:

def is_valid(maze, x, y, direction, dir_map):
	m = len(maze)
	n = len(maze[0])
	xp = x + dir_map[direction][0]
	yp = y + dir_map[direction][1]
	if xp >= 0 and xp < m and yp >= 0 and yp < n and maze[xp][yp] == 0:
		return True
	return False


def maze_route(maze, begin, end):
	dir_map = ((-1, 0), (0, 1), (1, 0), (0, -1))
	stack = [(begin[0], begin[1])]
	while len(stack) > 0:
		curr_x, curr_y = stack.pop()
		maze[curr_x][curr_y] = 1
		if curr_x == end[0] and curr_y == end[1]:
			return stack
		for d in range(4):
			if is_valid(maze, curr_x, curr_y, d, dir_map):
				stack.append((curr_x, curr_y))
				stack.append((
					curr_x + dir_map[d][0],
					curr_y + dir_map[d][1],
				))
				break
	return None


if __name__ == '__main__':
	maze = [
		[0, 0, 0, 0, 0],
		[0, 1, 1, 0, 0],
		[0, 0, 0, 0, 1],
		[0, 0, 1, 1, 0],
		[0, 0, 0, 0, 0],
	]
	print(maze_route(maze, (0, 0), (4, 4)))

在上述範例中:

這樣子的演算法,有個正式名稱是「深度優先搜尋(Depth-First Search, DFS)」,其時間複雜度在本範例而言,由於每個格子最多只會被 push 和 pop 各四次(唯起點若位在四邊都可以走的地方,則連同一開始的 push 在內,最多可能會有五次 push),因此是 O(mn),其中 m 和 n 分別是迷宮的 row 和 column 的數目。然而各位須留意的是,DFS 只保證若路徑存在的話一定會找到,但是不保證是最短路徑。