背景
一道很經典的數據結構的題目實現。
棧:一般是後進先出的順序,可以看下java中的Stack這個類。
隊列:一般是先進先出的順序,但是java中的Queue接口中也寫了註釋,沒有要求是必須嚴格的先進先出,比如java中也有優先級隊列、雙端隊列Deque。
代碼實現
在代碼的註釋中有描述對應的思路,這裏不去贅述
- 兩個棧實現隊列
/**
* Created by zlj on 2020/3/18.
* 兩個棧 實現 隊列
*
* 思路:
* 1.入棧:直接壓棧進入stackOne
* 2.出棧:
* (1)判斷stackOne是否爲空,如果不爲空,則將stackOne中的數據導入stackTwo。取出stackTwo彈棧的元素
* (2)判斷stackTwo是否爲空,如果不爲空,重複(1)操作
*
*/
public class StackToQueue<T> {
Stack<T> stackOne = new Stack<>();
Stack<T> stackTwo = new Stack<>();
/**
* 入隊
* @param data
*/
private void push(T data) {
stackOne.push(data);
}
/**
* 出隊列
* @return
*/
private T pop() {
if (stackOne.empty() && stackTwo.empty())
return null; // 容錯 棧的pop方法如果沒有元素了會報錯
// 其實加不加這個判斷相當於Queue接口中poll(做了容錯) 和 remove(沒做容錯) 兩個方法的區別
while (!stackOne.isEmpty()) {
// 彈棧到stackTwo
stackTwo.push(stackOne.pop());
}
// 這時彈棧 stackTwo中的元素
T stackTwoFirstEmt = stackTwo.pop();
// 如果出隊列的時候元素都在stackTwo
while(!stackTwo.isEmpty()) {
// 數據倒回stackOne 以便下一次pop的時候 再利用棧的特性 實現隊列出隊的順序
stackOne.push(stackTwo.pop());
}
return stackTwoFirstEmt;
}
public static void main(String[] args) {
StackToQueue<Integer> stackToQueue = new StackToQueue<>();
// 隊列入隊
Stream.iterate(1, i -> i+1).limit(10).forEach(stackToQueue::push);
// 隊列出隊
for(int i = 0; i< 10000; i++) {
Integer pop = stackToQueue.pop();
if (pop == null) break;
System.out.println("隊列出隊:" + pop);
try {
Thread.sleep(200);
} catch (Exception e) {
}
}
}
}
- 兩個隊列實現棧
/**
* Created by zlj on 2020/3/19.
* 兩個隊列實現棧
* 思路: 維護兩個隊列 Q1 和 Q2
* (1)入棧:即爲隊列Q1中加入元素
* (2)出棧: 關鍵就是保持隊列q1和q2一直有一個爲空
*
* - 首先元素入q1,隊列中爲 [tail]xn->xn-1...->x1 [head] 這時將q1中的n-1個元素入q2 q2中元素: [tail] xn-1->xn-2...->x1 [head]
* - xn從q1中出隊
* - 這時q1爲空,q2有n-1個元素,重複第一步,只不過現在是將q2中的n-2個元素出隊放入q1中,再將q2中的xn-1出隊即可。
* - 重複操作直到 q1和q2都爲空爲止
*/
public class TwoQueueToStack<T> {
// 這裏用的Deque 雖然只是需要的是隊列 先進先出(FIFO)的特性 但其實Deque既提供了stack的操作、又提供了queue的操作,也提供了對first和end的操作(LinkedList裏面叫做head和tail)
Queue<T> queue1 = new ArrayDeque<>();
Queue<T> queue2 = new ArrayDeque<>();
/**
* 棧的入棧操作
* @param element
*/
void push(T element) {
queue1.offer(element); // 比add更友好
}
/**
* 棧的出棧操作
*
* @return
*/
T pop() {
if (!queue1.isEmpty()) {
while (queue1.size() > 1) {
// q1出隊 入隊q2
queue2.offer(queue1.poll());
}
// q1的size是1了
return queue1.poll();
}
if (!queue2.isEmpty()) {
while (queue2.size() > 1) {
queue1.offer(queue2.poll());
}
// q2的size是1了
return queue2.poll();
}
return null;
}
int size() {
if (!queue1.isEmpty()) {
return queue1.size();
} else if (!queue2.isEmpty()) {
return queue2.size();
}
return 0;
}
/**
* 實現一個只查看棧頂元素的操作
* 思路也是先將q1的n-1個元素入隊q2,這時將q1中的剩餘元素peek出來,不是poll出來(元素不能刪除),再將其也導入到q2中
* // 注意這裏如果直接用的是雙端隊列Deque 其實直接可以在不爲空的隊列中 peekLast = =
*/
@SuppressWarnings("all")
T top() {
T top = null;
if (!queue1.isEmpty()) {
while (queue1.size() > 1) {
queue2.offer(queue1.poll());
}
top = queue1.peek();
// 再將其入隊至q2
queue2.offer(queue1.poll());
}
if (!queue2.isEmpty()) {
while (queue2.size()>1) {
queue1.offer(queue2.poll());
}
top = queue2.peek();
queue1.offer(queue2.poll());
}
return top;
}
public static void main(String[] args) {
TwoQueueToStack<Integer> twoQueueToStack = new TwoQueueToStack<>();
// 壓棧
Stream.iterate(1, i -> i+1).limit(10).forEach(twoQueueToStack::push);
// 查看棧頂的元素
System.out.println("棧頂元素:" + twoQueueToStack.top());
// 出棧
int size = twoQueueToStack.size();
for (int i = 0; i <size; i++) { // 注意這裏不能寫成 i < twoQueueToStack.size() 因爲循環中的pop操作會減少stack中的元素
System.out.println("元素出棧" + twoQueueToStack.pop());
}
}
}