最近看了個面試的帖子講的是“怎麼用兩個棧來實現佇列的操作”,好奇的我也想試下這道題目,咋一看這道題目挺簡單的呀,嗯,確實不難。先簡單講下我第一眼看到這個題目時想到的解法。講解法之前先給大家講下資料結構中的棧和佇列吧,免得有的人不明白棧和佇列,那就沒辦法繼續看下去了。
棧(stack)(wiki)
我們經常會在面試中聽到“棧”這個詞語,理解這個概念對於理解程式的執行至關重要。棧這個詞語在不同的語境中表達的含義是不同的。下面我將解釋下棧的三種含義(參考:《Stack的三種含義》)。
-
含義一:資料結構
我們這裡講的其實就是這種含義,stack是一種儲存資料的結構,特點是LIFO,即後進先出(last in First out)。
這種資料結構其底層是用連結串列來現實的,最大特點是隻能操作最上面的元素,所以先進棧的被壓在下面只能後出棧。
這種資料結構通常又下面幾種方法來操作最上面的元素:
push
: 進棧
pop
: 出棧
top
: 獲取棧頂元素
empty
:判斷棧是否為空
-
含義二:程式碼的執行方式
stack的第二種含義是“呼叫棧“,表示函式像積木一樣的堆放,以供層層呼叫,我們在函式中的遞迴就是用堆疊實現的,遞迴簡單點來說就是函式中呼叫該函式。
-
含義三:記憶體區域
stack的第三種含義就是儲存資料的一種記憶體區域。程式在執行時需要一定的空間來儲存資料。一般來說系統會劃分出兩種不同的記憶體空間,一種是棧(stack),另一種是堆(heap)。
棧是由作業系統自動分配和釋放的,存放函式的引數值,區域性變數值等。堆一般由程式設計師分配並釋放,若程式設計師不釋放,程式結束時可能由OS回收,分配方式類似於連結串列。我們經常講的記憶體洩漏其實就是指的就是堆。
介紹完了棧的定義,簡單說下棧和佇列的區別吧,棧的特點是先進的後出,佇列的特點是先進的先出。這道題目考查的重點還是對於棧和佇列特性的理解,其次再加上一些思考就好了。
看完這道題目我其實第一反應就覺得這道題挺簡單的,新建兩個棧s1、s2,然後當有入棧請求的時候先將s1棧中的資料依次出棧進入到s2的棧中,然後將新請求入棧的資料放入s1中,最後將s2棧中資料依次出棧進入到s1棧中。每次有出棧請求的時候將s1棧中的資料出棧就好了。這種解法應該是比較常規的解法,也比較容易想到的。
但是仔細想下這種方法效率比較低,有頻繁的出棧和入棧操作,對於效能的影響比較大,那有沒有什麼更好的方法能解決這個問題呢?其實我們可以在出棧的時候把棧底元素取出來,入棧的時候正常入棧。具體實現就是當有入棧請求的時候將資料壓入棧s1,當有出棧請求的時候將s1資料依次出棧壓入s2棧中,那麼這時s2的棧頂元素就是s1的棧底元素,最後將s2的資料又依次壓入s1中。這麼這樣的話就只有出棧的操作會有頻繁的棧操作,對於入棧來講沒有額外的棧操作。這種方法其實效率已經非常高了。
其實還有一種效率更高的方法來解決這個問題,仔細想下這麼問題,將上面的解法做個優化,將s1棧中的資料依次壓入s2中後又依次壓入s1中,那為什麼又要將s2中的資料重新壓回s1呢?其實這步操作可以避免,我們可以當有入棧的操作時將資料壓入s1,當有出棧操作時將s2中的資料出棧,若s2棧為空,則將s1中的資料全部依次壓入s2中,這樣的方法效率會比上面的方法更優。
下面的程式碼就是上面提到的最後一種方法的實現:
#include <iostream>
#include <cstdio>
#include <stack>
#include <algorithm>
using namespace std;
std::stack<int>s1, s2;
int main()
{
printf("Please input argument:1 push,2 pop\n");
int operation, num;
while(scanf("%d", &operation) != EOF)
{
if(operation == 1)
{
printf("please input queue push num:");
scanf("%d", &num);
s1.push(num);
}
else if(operation ==2)
{
if(!s2.empty())
{
printf("Queue pop num: %d\n", s2.top());
s2.pop();
}
else if(!s1.empty())
{
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
printf("Queue pop num: %d\n", s2.top());
s2.pop();
}
else
{
printf("queue empty\n");
}
}
else
{
printf("Input invalid, please input repeat");
}
}
return 0;
}
複製程式碼