今天稍微停下前進的腳步,來看下隊棧的左右互搏術。 前兩天學習了隊列和棧之後,今天就能夠試着來用兩個棧實現隊列的功能或者用兩個隊列來實現棧的功能。api
棧是先進後出,隊列是先進先出,但能夠用兩個棧來模擬一個隊列的功能,來實現隊列中主要的 enqueue,dequeue, head 方法。數組
咱們所學的每一種數據結構,本質上都是對數據如何存儲和使用的研究,這就必然涉及到增刪改查,那麼考慮實現這些方法時,咱們優先考慮如何實現數據的增長,只有存在了數據,纔可以後續的操做。 因此如何實現隊列中的增長數據方法 enqueue 呢?bash
接下來考慮隊列中的刪除 dequeue 方法數據結構
那隊列的 head 方法呢學習
注意到了嗎,這裏又用到了 分而治之 的思想,還記得以前在哪裏用過嗎? 對,就是在給棧添加獲取最小值方法的時候用過,當時也是用了兩個棧來實現。 這裏的話 enqueue 始終都操做 stack1,dequeue 和 head 方法始終都操做 stack2。ui
{
class StackQueue {
constructor() {
this.stack1 = new Stack();
this.stack2 = new Stack();
}
// 初始化stack,僞造私有方法
_initStack() {
if (this.stack1.isEmpty() && this.stack2.isEmpty()) {
return null; // 若是兩個棧都是空的,那麼隊列中就沒有元素
}
if (this.stack2.isEmpty()) {
// 若是stack2是空的,那麼此時stack1必定不爲空
while (!this.stack1.isEmpty()) {
this.stack2.push(this.stack1.pop()); // 把stack1的元素移除到stack2中
}
}
}
// 向隊尾添加一個元素
enqueue(item) {
this.stack1.push(item); // 把數據存入到stack1中
}
// 刪除隊首的一個元素
dequeue() {
this._initStack();
return this.stack2.pop();
}
// 返回隊首的元素
head() {
this._initStack();
return this.stack2.top();
}
}
var stackQueue = new StackQueue();
stackQueue.enqueue(1);
stackQueue.enqueue(4);
stackQueue.enqueue(8);
console.log(stackQueue.head()); // 1
stackQueue.dequeue();
stackQueue.enqueue(9);
console.log(stackQueue.head()); // 4
stackQueue.dequeue();
console.log(stackQueue.head()); // 8
console.log(stackQueue.dequeue()); // 8
console.log(stackQueue.dequeue()); // 9
}
複製代碼
是否是以爲很簡單呢,梳理清楚隊列和棧的特性就OK啦。 接下來讓咱們繼續修煉,用隊列實現棧吧!this
隊列是先進先出,棧是先進後出,(不斷重複這兩個知識點) 但能夠用兩個隊列來模擬一個棧的功能,來實現棧中主要的 push,pop, top 方法。spa
你可能會想到利用上邊的套路來實現這個需求,可是最後的結果你會發現是不正確的。由於 把 stack1 的元素移除到 stack2 中,此時的兩個棧中的數據就首尾交換了,而若是此處換成隊列 this.queue2. enqueue(this.queue1. dequeue()), 你會發現因爲隊列的特性,此時的兩個隊列仍是同樣的,首尾並無交換。code
so 咱們來換個思路隊列
和上邊同樣,咱們先考慮如何實現棧的存儲數據 push 方法:
top 方法就簡單了:
接下來思考比較複雜的 pop 方法:
在具體的實現中,須要額外定義兩個變量,dataQueue 和 emptyQueue:
{
class QueueStack {
constructor() {
this.queue1 = new Queue();
this.queue2 = new Queue();
this.dataQueue = null; // 存放數據的隊列
this.emptyQueue = null; // 存放備份數據的隊列
}
// 初始化隊列數據,模擬私有方法 確認哪一個隊列存放數據,哪一個隊列作備份
_initQueue() {
if (this.queue1.isEmpty()) {
this.dataQueue = this.queue2;
this.emptyQueue = this.queue1;
} else {
// 都爲空的話 默認是 隊列1
this.dataQueue = this.queue1;
this.emptyQueue = this.queue2;
}
}
// 往棧裏壓入一個元素
push(item) {
this._initQueue();
this.dataQueue.enqueue(item);
}
// 返回棧頂的元素
top() {
this._initQueue();
return this.dataQueue.tail();
}
// 把棧頂的元素移除
pop() {
this._initQueue();
while (this.dataQueue.size() > 1) {
// 利用備份隊列轉移數據,
this.emptyQueue.enqueue(this.dataQueue.dequeue()); // 數據隊列和備份隊列交換了身份
}
return this.dataQueue.dequeue(); // 移除數據隊列的頭部元素
}
}
var queueStack = new QueueStack();
queueStack.push(1);
queueStack.push(2);
queueStack.push(4);
console.log(queueStack.top()); // 棧頂是 4
console.log(queueStack.pop()); // 移除 4
queueStack.push(5);
console.log(queueStack.top()); // 棧頂變成 5
queueStack.push(6);
console.log(queueStack.pop()); // 移除 6
console.log(queueStack.pop()); // 移除5
console.log(queueStack.top()); // 棧頂是 2
}
複製代碼
若是你有其餘好的方法,歡迎留言
咱們利用基礎的數組 api 實現了隊列和棧的功能,再來回顧下(敲黑板,劃重點了)
固然還有其餘的隊列和棧,我這裏只介紹到了基礎的實現。這個硬骨頭,還需慢慢啃。
若是有錯誤或者錯別字,還請給我留言指出,謝謝。