在不考慮空間大小的前提下,你可以隨意的 push 一些東西進去 stack 當中;而在 stack 裡有東西的情況下,你也可以隨意的 pop 一些東西出來;那麼,如果給你那「一些東西」,讓你依序的 push 進 stack,並隨意的在 stack 不空的時候 pop 出來,直到所有東西都被 push 與 pop 各一次,則是否能製造出某些特定順序,又有某些順序不可能出現呢?

要做這個檢查其實非常的簡單,為了方便稱呼,以下稱讓你依序 push 進去的序列是 Spush,而等待被檢測是否可由這一連串 push/pop 的操作製造出來的序列為 Spop;並且,以下敘述僅考慮所有元素皆不相同的狀況。

首先,由於若 Spop 真的可以被這一連串 push/pop 的操作製造出來,則其首個元素 Spop[0] 一定是最先被 pop 出來的;因此我們需先將 Spush 依次的 push 進 stack,直到看到與 Spop[0] 相等的元素為止,然後將它 pop 出來。

接著,由於若 Spop 真的可以被這一連串 push/pop 的操作製造出來,則 Spop[1] 只可能出現在 stack 頂端,或者 Spush 當中還沒被 push 進去的部分;如果是前者的話,我們就從 stack pop 一個元素出來;如果是後者的話,就跟前一步一樣繼續把 Spush 依次的 push 進 stack,直到看到與 Spop[1] 相等的元素為止,然後將它 pop 出來。後續的步驟可以依此類推,當然若你發現前述兩種情況都不符合,則代表 Spop 無法經由對 Spush 做最一連串的 push/pop 來得到。

上述的步驟的實作如下,為了方便,範例中會把 Spush 固定為「ABCDE」,但此演算法不論 Spush 是否遵守特定順序,皆能正常運作,你也可以試著修改,或者直接將 Spush 變成函式的參數:

def is_spop_valid(s_pop):
	s_push = 'ABCDE'
	stack = []
	i_push = 0
	n = len(s_push)
	for i_pop in range(n):
		while i_push < n and (len(stack) == 0 or stack[-1] != s_pop[i_pop]):
			stack.append(s_push[i_push])
			i_push += 1
			if i_push == n:
				break
		if stack[-1] == s_pop[i_pop]:
			stack.pop()
		else:
			return False
	return True


if __name__ == '__main__':
	print(is_spop_valid('CABDE'))

Note: 請自己在 for 迴圈的開頭或某處加入一些 print,來確認迴圈每一輪的 stack 內容。

雖然 for 跟 while 搭配起來,像是巢狀迴圈的 O(n2),但由於每個元素只會被 push 和 pop 最多各一遍,而 push 及 pop 都是 O(1),因此演算法整體的時間複雜度是 O(n)。此外,你如果想對這個問題做更多的嘗試,也可以參考 LeetCode 的 946. Validate Stack Sequences