昨天去阿里巴巴參加Java研發工程師的實習生面試,遇到的一個題目:
我們要反轉一個棧,如果使用另外一個棧作為輔助的話,那么反轉起來很簡單,一個接一個push到輔助棧里再push回來就行了。那么假如不能使用輔助棧,數組等空間為O(n)的數據結構,只使用O(1)的空間復雜度即只能有常數個變量,怎么實現將棧反轉?即原來的棧頂在棧底,棧底變成棧頂。
面試官提示我使用遞歸來考慮。當時我沒有想出來……
這道題使得我對遞歸算法的理解,感覺提升了一小下。
讓我懂得了遞歸算法不能考慮中間過程,上一層的遞歸可以直接使用下一層的遞歸結果,即假設下一層已經完成了我們的要求就行了,最后只需要考慮最后一層遞歸退出的條件就行了。
回來以后想,這道題大概是這樣的:
先畫圖:
- 首先假設棧里面從棧底到棧頂存儲的依次是 1 2 3 4 5,我們反轉以后希望得到的結果是 5 4 3 2 1.
- 第一步pop出來第一個數5,存到temp1中,此時的棧中由下到上是1 2 3 4。
- 遞歸調用自身,不用考慮具體的過程,我們只需要知道這個遞歸調用結束后得到的結果是使棧中的元素變成了由下到上 4 3 2 1(因為這個函數本身的意思就是反轉棧)。
- 此時pop出來第二個數1 ,存到temp2中,此時的棧中由下到上是 4 3 2。
- 再遞歸調用本身,抽象考慮這個遞歸完成后得到的結果是棧中的值變成了 2 3 4。
- 接着我們把push(temp1),棧變成了 2 3 4 5。
- 接着遞歸調用本身,遞歸完成后棧變成了 5 4 3 2。
- 接着push(temp2)。好了,棧變成了 5 4 3 2 1,反轉完成!
接下來我們需要考慮遞歸函數結束的條件了,該函數的結束條件是當棧為空或者棧里只有一個元素的時候,return。
如何知道棧里只有一個元素?
我們可以先記錄下棧頂元素,然后pop,如果棧變成空,則說明是只有一個元素,push回去再return。
結合上圖和思路,給出代碼:
#include <iostream> #include <stack> using namespace std; void printInfo(stack<int> s){ int a ; while(!s.empty()){ a = s.top(); cout << a << " . " ; s.pop(); } cout << endl; return ; } void reverseStack(stack<int> &s){ if(s.empty()) return ; else { //如果s里面只有一個元素,就返回,否則就不返回。具體實現是先pop出來一個,判斷剩下的是不是空棧。 int a = s.top(); s.pop(); if(s.empty()){ s.push(a); return ; } else{ s.push(a); } } int temp1 = s.top(); s.pop(); reverseStack(s); int temp2 = s.top(); s.pop(); reverseStack(s); s.push(temp1); reverseStack(s); s.push(temp2); } int main(){ stack<int> s; s.push(1); s.push(2); s.push(3); s.push(4); s.push(5); cout << "-------------before recursion------------" << endl; printInfo(s); cout << "-------------after recursion------------" << endl; reverseStack(s); printInfo(s); system("pause"); return 0; }
運行結果: