隊列實現(xiàn)棧的3種方法,全都擊敗了100%的用戶!
本文轉(zhuǎn)載自微信公眾號「Java中文社群」,作者磊哥。轉(zhuǎn)載本文請聯(lián)系Java中文社群公眾號。
今天我們要講的是「用隊列實現(xiàn)棧」,它們都屬于常見的面試題,而我們今天要用多種方法來實現(xiàn)隊列到棧的“轉(zhuǎn)變”。
老規(guī)矩,先來回顧一下棧(Stack)和隊列(Queue)的特性和常見方法。
棧是后進(jìn)先出(LIFO)的數(shù)據(jù)結(jié)構(gòu),常見方法如下:
- push():入棧方法,向棧頂添加元素;
- pop():出棧方法,將棧頂?shù)脑匾瞥⒎祷卦?
- peek():查詢棧頂元素,并不會移除元素。
隊列是先進(jìn)先出(FIFO)的數(shù)據(jù)結(jié)構(gòu),常見方法如下:
- offer():入隊方法,向隊尾添加元素;
- poll():出隊方法,從隊頭移除并返回元素;
- peek():查詢隊頭元素,并不會移除元素。
知道了這些基礎(chǔ)內(nèi)容之后,就來看今天的問題吧。
題目描述使用隊列實現(xiàn)棧的下列操作:
- push(x) -- 元素 x 入棧
- pop() -- 移除棧頂元素
- top() -- 獲取棧頂元素
- empty() -- 返回棧是否為空
注意:
- 你只能使用隊列的基本操作-- 也就是 push to back, peek/pop from front, size, 和 is empty 這些操作是合法的;
- 你所使用的語言也許不支持隊列。你可以使用 list 或者 deque(雙端隊列)來模擬一個隊列 , 只要是標(biāo)準(zhǔn)的隊列操作即可;
- 你可以假設(shè)所有操作都是有效的(例如, 對一個空的棧不會調(diào)用 pop 或者 top 操作)。
LeetCode 225:https://leetcode-cn.com/problems/implement-stack-using-queues/
題目解析
這道題的題目很好理解:只需要將先進(jìn)先出的隊列,轉(zhuǎn)換為后進(jìn)先出的“棧”就可以了,我們可以從多個方向入手來實現(xiàn)此功能,比如使用兩個隊列插入并交換的方式,或者是一個隊列插入再交換的方式,或雙端隊列的方式來實現(xiàn)此功能,具體實現(xiàn)方法和代碼如下。
實現(xiàn)方法 1:兩個隊列實現(xiàn)棧
之前我們用兩個棧實現(xiàn)了一個隊列的文章中,主要使用的是「負(fù)負(fù)得正」的思想,那么當(dāng)看到此道題時,首先應(yīng)該想到的是用兩個隊列來實現(xiàn)一個棧,但這里的實現(xiàn)思路和用棧實現(xiàn)隊列的思路又略有不同,因為隊列都是先進(jìn)先出的,我們可以把它理解為「正數(shù)」,那么也就不能用「負(fù)負(fù)得正」的思想了,我們這里使用兩個隊列來實現(xiàn)棧,主要的操作思路是先將元素插入一個臨時隊列中,然后再將另一個隊列的所有元素插入到臨時隊列的尾部,從而實現(xiàn)后進(jìn)先出功能的,具體的實現(xiàn)步驟如下。
步驟一
添加首個元素,入列到臨時隊列 queue2:
步驟二
因為正式隊列中無元素,因此無需將 queue1 中的元素移動到臨時隊列 queue2 的尾部,直接將臨時隊列和正式隊列互換即可:
步驟三
添加第二個元素,先入列到臨時隊列 queue2:
步驟四
再將 queue1 中的元素移動到 queue2 的尾部,如下所示:
步驟五
再將 queue1 和 queue2 進(jìn)行互換:
步驟六
執(zhí)行出隊操作:
最終效果
從最終的效果圖我們可以看出,通過兩個隊列已經(jīng)實現(xiàn)了后進(jìn)先出的特性,也就是完成了從隊列到棧的轉(zhuǎn)換,實現(xiàn)代碼如下:
- import java.util.Queue;
- class MyStack {
- Queue<Integer> queue1;
- Queue<Integer> queue2;
- public MyStack() {
- queue1 = new LinkedBlockingQueue();
- queue2 = new LinkedBlockingQueue();
- }
- /**
- * 入棧
- */
- public void push(int x) {
- // 1.入列臨時隊列二
- queue2.offer(x);
- // 2.將隊列一的所有元素移動到隊列二
- while (!queue1.isEmpty()) {
- queue2.offer(queue1.poll());
- }
- // 3.隊列一和隊列二互換
- Queue<Integer> temp = queue1;
- queue1 = queue2;
- queue2 = temp;
- }
- /**
- * 出棧并返回此元素
- */
- public int pop() {
- return queue1.poll();
- }
- /**
- * 查詢棧頂元素
- */
- public int top() {
- return queue1.peek();
- }
- /**
- * 判斷是否為空
- */
- public boolean empty() {
- return queue1.isEmpty();
- }
- }
我們在 LeetCode 中提交以上測試代碼,執(zhí)行結(jié)果如下:
此方法很穩(wěn),竟然擊敗了 100% 的用戶。
實現(xiàn)方法 2:一個隊列實現(xiàn)棧
那我們可以不可以用一個隊列來實現(xiàn)棧呢?答案是肯定的。
我們只需要執(zhí)行以下兩個步驟就可以實現(xiàn)將隊列轉(zhuǎn)換為棧了,具體實現(xiàn)步驟如下:
將元素入列到隊尾;
再將除隊尾之外的所有元素移除并重寫入列。
這樣操作之后,最后進(jìn)入的隊尾元素反而變成了隊頭元素,也就實現(xiàn)了后進(jìn)先出的功能了,如下圖所示。
步驟一
元素 1 入列:
步驟二
元素 2 入列:
步驟三
將最后一個元素之前的所有元素,也就是元素 1,出列重新入列:
步驟四
執(zhí)行出隊操作:
最終效果
以上思路的實現(xiàn)代碼如下:
- import java.util.Queue;
- class MyStack {
- Queue<Integer> queue1;
- public MyStack() {
- queue1 = new LinkedBlockingQueue();
- }
- /**
- * 入棧
- */
- public void push(int x) {
- // 獲取原隊列長度(要移動的次數(shù))
- int count = queue1.size();
- // 將元素放入隊尾
- queue1.offer(x);
- // 將除最后一個元素外,其他的元素重新入隊
- for (int i = 0; i < count; i++) {
- System.out.println("for");
- queue1.offer(queue1.poll());
- }
- }
- /**
- * 出棧并返回此元素
- */
- public int pop() {
- return queue1.poll();
- }
- /**
- * 查詢棧頂元素
- */
- public int top() {
- return queue1.peek();
- }
- /**
- * 判斷是否為空
- */
- public boolean empty() {
- return queue1.isEmpty();
- }
- }
- 我們把以上代碼在 LeetCode
我們把以上代碼在 LeetCode 中提交,執(zhí)行結(jié)果如下:
此方法依舊很穩(wěn),也是同樣的擊敗了 100% 的用戶,只不過此方法在內(nèi)存方面有更好的表現(xiàn)。
實現(xiàn)方法 3:雙端隊列實現(xiàn)棧
如果覺得以上方法比較難的話,最后我們還有一個更簡單的實現(xiàn)方法,我們可以使用 Java 中的雙端隊列 ArrayDeque 來實現(xiàn)將元素可以插入隊頭或隊尾,同樣移除也是,那么這樣我們就可以從隊尾入再從隊尾出,從而就實現(xiàn)了棧的功能了。
雙端隊列結(jié)構(gòu)如下:
我們來演示一下用雙端隊列實現(xiàn)棧的具體步驟。
步驟一
元素 1 入隊:
步驟二
元素 2 入隊(隊尾):
步驟三
再從隊尾出隊:
最終效果
以上思路的實現(xiàn)代碼如下:
- import java.util.ArrayDeque;
- class MyStack {
- ArrayDeque<Integer> deque;
- public MyStack() {
- deque = new ArrayDeque<>();
- }
- /**
- * 入棧
- */
- public void push(int x) {
- deque.offer(x);
- }
- /**
- * 出棧并返回此元素
- */
- public int pop() {
- return deque.pollLast();
- }
- /**
- * 查詢棧頂元素
- */
- public int top() {
- return empty() ? -1 : deque.peekLast();
- }
- /**
- * 判斷是否為空
- */
- public boolean empty() {
- return deque.isEmpty();
- }
- }
我們把以上代碼在 LeetCode 中提交,執(zhí)行結(jié)果如下:
總結(jié)
本文我們用 3 種方法實現(xiàn)了將隊列轉(zhuǎn)換為棧,其中最簡單的方法是用 Java 中自帶的雙端隊列 ArrayDeque 從隊尾入并從隊尾出就實現(xiàn)了棧 ,其他兩個方法使用的是普通隊列,通過入隊之后再移動元素到入隊元素之后的方法,從而實現(xiàn)了棧的功能。