剑指 Offer 09. 用两个栈实现队列 - 力扣(LeetCode)
发布:2021年9月24日20:43:05
问题描述及示例
用两个栈实现一个队列。队列的声明如下,请实现它的两个函数 appendTail 和 deleteHead ,分别完成在队列尾部插入整数和在队列头部删除整数的功能。(若队列中没有元素,deleteHead 操作返回 -1 )
示例 1:
输入:
[“CQueue”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[3],[],[]]
输出:[null,null,3,-1]
示例 2:
输入:
[“CQueue”,“deleteHead”,“appendTail”,“appendTail”,“deleteHead”,“deleteHead”]
[[],[],[5],[2],[],[]]
输出:[null,-1,null,null,5,2]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
提示:
1 <= values <= 10000
最多会对 appendTail、deleteHead 进行 10000 次调用
我的题解(双栈/双数组)
总体思路比较简单,要用两个栈来实现队列结构,而对于栈,我们只能操作栈顶元素,且栈元素只能是先进后出。所以说,必须用一个栈(appendStack
)来完成入队列的操作,而另一个栈(deleteStack
)来完成出队列的操作。当然这两个栈结构都是用JavaScript中的数组结构来模拟的。
当我们往队列中存入元素时,其实就是往 appendStack
存入元素。而当我们想要从队列中删除元素时,其实是要删除 appendStack
栈底的元素,而我们只能操作栈顶,所以就需要 deleteStack
来做辅助:
- 先把
appendStack
中的元素逐个取出并压入deleteStack
,此时deleteStack
中的栈顶元素就是我们要删除的元素; - 再把
deleteStack
中的栈顶元素弹出,并用一个变量head
保存,将来作为返回值返回; - 最后再将
deleteStack
中的元素逐个取出并压入appendStack
中。
所以说,appendStack
中存储的始终是整个队列的所有元素,且其顺序也保持为入队顺序。
详解请看下方注释:
var CQueue = function() {
// appendStack用于完成入队列的逻辑
this.appendStack = [];
// deleteStack用于完成出队列的逻辑
this.deleteStack = [];
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
// 入队列时只要把元素压入appendStack即可
this.appendStack.push(value);
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
// 出队列时,如果发现appendStack为空,说明队列为空,直接返回-1
if(!this.appendStack.length) {
return -1;
}
// 否则的话,先将appendStack中的元素逐个取出并压入deleteStack
while(this.appendStack.length) {
this.deleteStack.push(this.appendStack.pop());
}
// 把deleteStack的栈顶元素弹出,该元素即为队列的队首元素
let head = this.deleteStack.pop();
// 再将deleteStack中的剩余元素重新逐个取出并压入appendStack
while(this.deleteStack.length) {
this.appendStack.push(this.deleteStack.pop());
}
// 返回弹出的队首元素
return head;
};
提交记录
55 / 55 个通过测试用例
状态:通过
执行用时:440 ms, 在所有 JavaScript 提交中击败了10.23%的用户
内存消耗:50 MB, 在所有 JavaScript 提交中击败了9.91%的用户
时间:2021/09/24 20:46
可以看到这种做法的性能不大行,我想原因就在于每次删除删除队首元素时,都要把两个栈给各自遍历一遍,试想一下,如果我们连续对队列中的元素进行删除操作,那么就要重复多次那样的遍历。既然这样的话,其实在每次删除完队首元素后,不用那么着急地把 deleteStack
中的元素给放回 appendStack
,而是根据操作的类型来动态决定是否需要把元素返回 appendStack
。
优化操作
具体这样做:
如果要入队的话,先判断 appendStack
和 deleteStack
是否为空:
- 如果
appendStack
不为空,那就说明队列中的元素都存在appendStack
中,此时就直接往appendStack
中存入元素; - 如果
appendStack
为空,且deleteStack
也为空,则说明队列中压根就没有元素,此时就直接往appendStack
中存入元素; - 如果
appendStack
为空,且deleteStack
不为空,那就说明队列中的元素都存在deleteStack
中,此时先将deleteStack
中的元素逐个取出并压入appendStack
中,然后再把元素压入appendStack
中以完成入队操作。
如果要出队的话,也是先判断 appendStack
和 deleteStack
是否为空:
- 如果
deleteStack
不为空,那就说明队列中的元素都存在deleteStack
中,此时就直接删除deleteStack
的栈顶元素并将栈顶元素作为返回值返回; - 如果
deleteStack
为空,且appendStack
也为空,则说明队列中压根就没有元素,此时就直接返回-1
; - 如果
deleteStack
为空,且appendStack
不为空,那就说明队列中的元素都存在appendStack
中,此时先将appendStack
中的元素逐个取出并压入deleteStack
中,然后再删除deleteStack
的栈顶元素并将栈顶元素作为返回值返回。
具体代码为:
var CQueue = function() {
this.appendStack = [];
this.deleteStack = [];
};
/**
* @param {number} value
* @return {void}
*/
CQueue.prototype.appendTail = function(value) {
if(!this.appendStack.length && this.deleteStack.length) {
while(this.deleteStack.length) {
this.appendStack.push(this.deleteStack.pop());
}
}
this.appendStack.push(value);
};
/**
* @return {number}
*/
CQueue.prototype.deleteHead = function() {
if(!this.deleteStack.length) {
if(!this.appendStack.length) {
return -1;
}
while(this.appendStack.length) {
this.deleteStack.push(this.appendStack.pop());
}
}
return this.deleteStack.pop();
};
提交记录
55 / 55 个通过测试用例
状态:通过
执行用时:440 ms, 在所有 JavaScript 提交中击败了10.23%的用户
内存消耗:49.9 MB, 在所有 JavaScript 提交中击败了12.57%的用户
时间:2021/09/24 22:15
但是似乎性能没有多大提升……em……可能是测试用例的原因吧……😅👀
官方题解
更新:2021年7月29日18:43:21
因为我考虑到著作权归属问题,所以【官方题解】部分我不再粘贴具体的代码了,可到下方的链接中查看。
更新:2021年9月24日20:47:52
【更新结束】
有关参考
暂无