前言
棧和隊列是算法的一個基本的知識點之一。這篇文章主要介紹三道有關棧和隊列的算法題。因為篇幅所限,只介紹push和pop這兩種方法的實現
-
用棧實現隊列
-
用隊列實現棧
-
循環隊列的實現
用棧實現隊列
入隊列的功能我們可以用棧的入棧的功能替代。但問題在於出隊列的功能怎么實現。
這里有一個問題,就是棧是后入先出的,隊列是先進先出的,兩者出入的方式不一樣。
那么怎么實現方向的一致呢? 我們可以使用兩個棧,一個棧用於輸入,一個棧用於輸出。當發現輸出棧為空的時候,把排列的數據從輸入棧一個個彈出,“倒入”到 輸出棧中,這樣的話,數據排列的方向就剛好被逆轉過來了,原本在輸入棧中處於棧底的數據被置換到輸出棧的棧頂,這時候對它出棧也就同時完成了隊列的出列的功能。
下面是具體的圖示
1.入隊列操作: 等同於對入隊列進行入棧操作,圖示如下

2.出隊列操作: 判斷當輸出棧為空時,先把輸入棧的數據依次彈出並加入到輸出棧中

步驟1

步驟2
對輸出棧棧頂進行出棧操作,即可完成出隊列功能

步驟3
具體代碼
import java.util.Stack; class MyQueue { private Stack<Integer> stack1; private Stack<Integer> stack2; private boolean isPushState = true; /** Initialize your data structure here. */ public MyQueue() { // 輸入棧 stack1 = new Stack<Integer>(); // 輸出棧 stack2 = new Stack<Integer>(); } /** Push element x to the back of queue. */ public void push(int x) { stack1.push(x); } public void transformStack () { if (stack2.empty()) { while (!stack1.empty()) { int t = stack1.pop(); stack2.push(t); } } } /** Removes the element from in front of queue and returns that element. */ public int pop() { transformStack(); return stack2.pop(); } /** Returns whether the queue is empty. */ public boolean empty() { return stack1.empty() && stack2.empty(); } }
用隊列實現棧
這里同樣有兩個功能需要我們實現,分別是入棧功能和出棧功能
入棧功能
我們可以用入棧操作模擬實現

出棧功能
我們又來到了關鍵功能,這時候你能猜到,我們又需要用到兩個隊列去實現這個出棧功能了
但問題在於,我們還能否通過講數據從一個隊列“倒”到另一個隊列的方式逆轉數據方向呢? 這是不行的,因為隊列先入先出的特性,導致分割后隊列的出入方向仍然是不變的。所以我們要換個思路:
一個元素入隊列了,我們接下來希望這個隊列像棧一樣通過pop把它直接彈出來,但是前面還有很多個元素排着隊呢,這時我們想,只要把前面排隊的所有元素先出列到另外一個輔助隊列里面去,接下來不就可以直接把這個元素踢出隊列從而模擬出棧了嗎?


當然完成pop操作后我們還要做一件事情,就是輔助隊列和主隊列互換,以讓未來還能按同樣的流程再來一次。

具體代碼
import java.util.LinkedList; import java.util.Queue; class MyStack { private Queue queue1; private Queue queue2; /** Initialize your data structure here. */ public MyStack() { queue1 = new LinkedList<Integer>(); queue2 = new LinkedList<Integer>(); } /** Push element x onto stack. */ public void push(int x) { queue1.offer(x); } /** Removes the element on top of the stack and returns that element. */ public int pop() { while (queue1.size()>1) { queue2.offer(queue1.poll()); } int result = (Integer) queue1.poll(); Queue temp =queue1; queue1 = queue2; queue2 = temp; return result; } /** Returns whether the stack is empty. */ public boolean empty() { return queue1.isEmpty(); } }
循環隊列的實現
設計循環隊列的原因
對於普通的單向隊列,在入隊列和出隊列的時候可能會遇到一種“隊列假滿”的現象,導致空間的浪費如下圖所示

所以我們要做的,就是通過設置頭部(front)和尾部(rear)兩個指針來區分隊列的“滿的部分”和“空的部分”,並且允許在填充到數組末尾的時候,能通過回到數組的頭部這種循環的方式繼續填充數組,從而提高數組空間的利用率。

怎么實現從尾部到頭部的循環呢? 我們可以通過取余運算符實現,因為當 i = 數組長度- 1的時候,(i + 1) % 數組長度 = 0,也就是說這個時候指針又從尾部回到了數組的頭部

具體代碼
class MyCircularQueue { private int [] queue; private int front; private int rear; private int len; /** Initialize your data structure here. Set the size of the queue to be k. */ public MyCircularQueue(int k) { queue = new int[k+1]; // 包含 front = 0; // 不包含 rear = 0; len = k+1; } /** Insert an element into the circular queue. Return true if the operation is successful. */ public boolean enQueue(int value) { if (isFull()) return false; queue[rear] = value; rear =(rear+1) % len; return true; } /** Delete an element from the circular queue. Return true if the operation is successful. */ public boolean deQueue() { if (isEmpty()) return false; queue[front] = -1; front = (front + 1) % len; return true; } /** Get the front item from the queue. */ public int Front() { if (isEmpty()) return -1; return queue[front]; } /** Get the last item from the queue. */ public int Rear() { if (isEmpty()) return -1; if (rear>0) return queue[rear-1]; else return queue[len - 1]; } /** Checks whether the circular queue is empty or not. */ public boolean isEmpty() { if (rear == front) return true; else return false; } /** Checks whether the circular queue is full or not. */ public boolean isFull() { if ((rear + 1)%len== front) return true; else return false; } }