【算法-剑指 Offer】09. 用两个栈实现队列(数组)

剑指 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

优化操作

具体这样做:

如果要入队的话,先判断 appendStackdeleteStack 是否为空:

  • 如果 appendStack 不为空,那就说明队列中的元素都存在 appendStack 中,此时就直接往 appendStack 中存入元素;
  • 如果 appendStack 为空,且 deleteStack 也为空,则说明队列中压根就没有元素,此时就直接往 appendStack 中存入元素;
  • 如果 appendStack 为空,且 deleteStack 不为空,那就说明队列中的元素都存在 deleteStack 中,此时先将 deleteStack 中的元素逐个取出并压入 appendStack 中,然后再把元素压入 appendStack 中以完成入队操作。

如果要出队的话,也是先判断 appendStackdeleteStack 是否为空:

  • 如果 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

参考:用两个栈实现队列 - 用两个栈实现队列 - 力扣(LeetCode)

【更新结束】

有关参考

暂无

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值