Stack 與遞迴的關係,其實非常的密切。事實上,遞迴的呼叫,甚至一般函式的層層呼叫,就是透過在系統中維護 stack 來完成,亦即一個函式在呼叫其他函式之前,會先把自己目前的一切狀態 push 進 stack,等待被呼叫的那個函式執行完畢後,再用 pop 還原回來,繼續做剩下的部分;也就是呼叫函式相當於 push,而 return 相當於 pop。而其底層實作在不同的硬體架構上可能是不一樣的,例如 MIPS 的設計,相當於你要自己維護 top 變數;而 8051 和 x86 則有專門可用於 push 與 pop 的相關指令,但是容許使用的記憶體空間大小等方面不太一樣。

而因為 stack 跟遞迴的關係密切,所以一個需要自己維護 stack 的程式,是可以改成遞迴版,讓原本自己寫的 stack,改成用系統來幫你維護的(但哪種版本看起來比較簡單就不一定了);而時間複雜度的部分,只要你能確保自己維護 stack 的版本(以下簡稱「stack 版」)跟遞迴版的操作有正確對應,則也會相同。

以括號配對為例,可以改寫為遞迴版如下:

def chk_parentheses_match(p_str, i=0, num_left_par=0):
	while i < len(p_str):
		if p_str[i] == '(':
			i = chk_parentheses_match(p_str, i=i+1, num_left_par=num_left_par+1)
			if i == -1:
				return -1
		else:
			if num_left_par == 0:
				return -1
			return i + 1
	if num_left_par == 0:
		return 0
	return -1


if __name__ == '__main__':
	print(chk_parentheses_match('((())(()))'))

在上述範例中:

中序轉後序的演算法設計,則是為了避免系統維護 stack 的額外損耗,才從實際建出一棵樹來跑遞迴呼叫,改為自行維護 stack。我們以沒有括號的 2 + 3 - 4 * 5 / 6 為例,將 stack 版的演算法過程與運算樹(也就是遞迴樹)上的走訪,做簡要的對比如下;其中,樹的動畫與「運算樹與前、中、後序式」篇章中,講解肉眼看著運算樹輸出後序式的方法時的那棵樹相同:


   

在前面的說明中,樹上的離開節點往上回傳,可以對應到 stack 版的輸出或 pop;而每個 operator 的左邊處理完畢,要往右邊走時,則對應到 stack 版的 push。

老鼠走迷宮的 DFS 演算法,當然也可以改成遞迴版:

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))
	curr_x, curr_y = begin
	if curr_x == end[0] and curr_y == end[1]:
		return [(curr_x, curr_y)]
	maze[curr_x][curr_y] = 1
	for curr_dir in range(4):
		if is_valid(maze, curr_x, curr_y, curr_dir, dir_map):
			result = maze_route(
				maze,
				(curr_x + dir_map[curr_dir][0], curr_y + dir_map[curr_dir][1]),
				end
			)
			if result is not None:
				return [begin] + result
	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)))

在上述範例中: