更多 LeetCode 题解笔记可以访问我的 github

描述

使用栈实现队列的下列操作:

  • push(x) -- 将一个元素放入队列的尾部。
  • pop() -- 从队列首部移除元素。
  • peek() -- 返回队列首部的元素。
  • empty() -- 返回队列是否为空。

示例:

  1. MyQueue queue = new MyQueue();
  2. queue.push(1);
  3. queue.push(2);
  4. queue.peek(); // 返回 1
  5. queue.pop(); // 返回 1
  6. queue.empty(); // 返回 false

说明:

  • 你只能使用标准的栈操作 -- 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
  • 你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
  • 假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)。

解法一:在一个栈中维持所有元素的出队顺序

思路

队列是一种先入先出(first in first out, FIFO)的数据结构,而栈是一种后入先出(last in first out, LIFO)的数据结构。因此,如果要使用栈来达到队列的效果,即用有后入先出性质的数据结构来实现先入先出的效果,需要借用两个栈来改变元素的出队顺序。当然,借用两个栈来实现队列也有不同的实现方式,这一节介绍第一种实现方式,在下一小节介绍第二种方式。

第一种方式是在一个栈中维持所有元素的出队顺序,即所有的元素在入队操作完成后只会保存在一个栈中,且其出栈的顺序和出队的顺序是一致的。下面对入队、出队等操作的底层实现分别进行讲解。

入队(push)

为了实现出栈顺序和出队顺序是一致的,入栈时必须将新的元素压入栈底。为了实现这种效果,在入队时,首先将栈1(假设栈1中保存所有的元素)中所有的元素弹出并压入栈2中,接着将新的元素压入栈1中,最后再将栈2中的所有弹出并压入栈1中。详细的步骤如图1所示。

图1:将一个元素入队

代码(Java)实现如下。

  1. public void push(int x) {
  2. // 将栈1中的所有元素弹出并压入栈2中
  3. while (!s1.isEmpty()) {
  4. s2.push(s1.pop());
  5. }
  6. // 将新的元素压入栈1
  7. s1.push(x);
  8. // 将栈2的所有元素弹出并压入栈1
  9. while (!s2.isEmpty()) {
  10. s1.push(s2.pop());
  11. }
  12. }

复杂度分析如下:

  • 时间复杂度:\(O(n)\),其中 \(n\) 表示入队时队列元素的数目,即栈1中元素的数目。入队时,栈1中的元素需要进行出栈和入栈两次,需要 \(4n\) 次操作,再加上新的元素的一次入栈操作,总的操作次数为 \(4n + 1\) 次。由于栈的入栈和出栈的时间复杂度是 \(O(1)\) 的,因此,入队的时间复杂度是 \(O(n)\) 的
  • 空间复杂度:\(O(n)\)

出队(pop)

出队操作比较简单,由于栈1中元素的出栈顺序和队列的出队顺序一致,因此,只需要弹出栈顶元素即可完成出队操作。

  1. public int pop() {
  2. if (s1.isEmpty()) {
  3. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  4. }
  5. return s1.pop();
  6. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\)
  • 空间复杂度:\(O(1)\)

查看队首(peek)

与出队操作类似,只需要查看栈1栈顶的元素即可完成查看队首的操作。

  1. public int peek() {
  2. if (s1.isEmpty()) {
  3. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  4. }
  5. return s1.peek();
  6. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\)
  • 空间复杂度:\(O(1)\)

是否为空(empty)

由于栈1中保存队列的所有元素,因此只需要判断栈1是否为空即可知道队列是否为空。

  1. public boolean empty() {
  2. return s1.isEmpty();
  3. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\)
  • 空间复杂度:\(O(1)\)

Java 实现

  1. class MyQueue {
  2. private Stack<Integer> s1;
  3. private Stack<Integer> s2;
  4. /** Initialize your data structure here. */
  5. public MyQueue() {
  6. s1 = new Stack<>();
  7. s2 = new Stack<>();
  8. }
  9. /** Push element x to the back of queue. */
  10. public void push(int x) {
  11. while (!s1.isEmpty()) {
  12. s2.push(s1.pop());
  13. }
  14. s1.push(x);
  15. while (!s2.isEmpty()) {
  16. s1.push(s2.pop());
  17. }
  18. }
  19. /** Removes the element from in front of queue and returns that element. */
  20. public int pop() {
  21. if (s1.isEmpty()) {
  22. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  23. }
  24. return s1.pop();
  25. }
  26. /** Get the front element. */
  27. public int peek() {
  28. if (s1.isEmpty()) {
  29. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  30. }
  31. return s1.peek();
  32. }
  33. /** Returns whether the queue is empty. */
  34. public boolean empty() {
  35. return s1.isEmpty();
  36. }
  37. }

Python 实现

  1. class MyQueue:
  2. def __init__(self):
  3. """
  4. Initialize your data structure here.
  5. """
  6. self._s1, self._s2 = [], []
  7. def push(self, x):
  8. """
  9. Push element x to the back of queue.
  10. :type x: int
  11. :rtype: void
  12. """
  13. while self._s1:
  14. self._s2.append(self._s1.pop())
  15. self._s1.append(x)
  16. while self._s2:
  17. self._s1.append(self._s2.pop())
  18. def pop(self):
  19. """
  20. Removes the element from in front of queue and returns that element.
  21. :rtype: int
  22. """
  23. return self._s1.pop()
  24. def peek(self):
  25. """
  26. Get the front element.
  27. :rtype: int
  28. """
  29. return self._s1[-1]
  30. def empty(self):
  31. """
  32. Returns whether the queue is empty.
  33. :rtype: bool
  34. """
  35. return not self._s1

解法二:一个栈入,一个栈出

思路

解法二的实现方式与解法一有点不同,按照功能的不同,解法二将两个栈一个用于入队,一个用于出队。假设栈 inStack 用于实现入队操作,栈 outStack 用于实现出队操作。下面对入队、出队等操作的底层实现分别进行讲解。

入队(push)

入队操作比较简单,直接将新的元素压入栈 inStack 中,同时,对于第一个进入栈中的元素,我们用一个变量 front 保存起来,用于表示栈 inStack 这个队列的队首。

  1. /** Push element x to the back of queue. */
  2. public void push(int x) {
  3. if (inStack.empty()) {
  4. front = x;
  5. }
  6. inStack.push(x);
  7. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\)
  • 空间复杂度:\(O(n)\),需要额外的空间用于存储队列元素

出队(pop)

在入队时,由于先入的元素处于输入栈 inStack 的栈底,因此,为了能够弹出栈底的元素实现出队操作,需要将输入栈 inStack 中的元素弹出并压入到输出栈 outStack 中。此时,输出栈 outStack 中元素的出栈顺序和队列的出队顺序是一致的。只要输出栈 outStack 中还有元素,每次执行出队操作只需要将栈 outStack 的栈顶元素弹出即可。当输出栈 outStack 为空时,执行出队操作则需要先将输入栈 inStack 中的元素弹出并压入输出栈。详细的步骤如图2所示。

图2:将一个元素出队

代码(Java)实现如下。

  1. /** Removes the element from in front of queue and returns that element. */
  2. public int pop() {
  3. if (empty()) {
  4. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  5. }
  6. if (outStack.isEmpty()) {
  7. while (!inStack.isEmpty()) {
  8. outStack.push(inStack.pop());
  9. }
  10. }
  11. return outStack.pop();
  12. }

复杂度分析如下:

  • 时间复杂度:均摊时间复杂度为 \(O(1)\),最坏情况下,时间复杂度为 \(O(n)\),更为详细的均摊复杂度分析可以查看官网的文章
  • 空间复杂度:\(O(1)\)

查看队首(peek)

与出队操作类似,当输出栈 outStack 不为空时,只需要返回输出栈 outStack 的栈顶元素即可。不同的是,由于我们用变量 front 存储了输入栈最先进入的元素,因此,当输出栈 outStack 为空时,不需要再将输入栈 inStack 的元素弹出并压入到输出栈 outStack 中便可以得到当前队首的元素。

  1. /** Get the front element. */
  2. public int peek() {
  3. if (empty()) {
  4. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  5. }
  6. if (!outStack.isEmpty()) {
  7. return outStack.peek();
  8. } else {
  9. return front;
  10. }
  11. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\),借助于变量 front,可以使得 peek 操作在任意情况下都是 \(O(1)\) 的时间复杂度
  • 空间复杂度:\(O(1)\)

是否为空(empty)

由于两个都有可以存在元素,因此,要判断队列是否为空,需要同时判断两个栈。

  1. /** Returns whether the queue is empty. */
  2. public boolean empty() {
  3. return inStack.isEmpty() && outStack.isEmpty();
  4. }

复杂度分析如下:

  • 时间复杂度:\(O(1)\)
  • 空间复杂度:\(O(1)\)

Java 实现

  1. class MyQueue {
  2. /**
  3. * The stack used to implement enqueue functionality
  4. */
  5. private Stack<Integer> inStack;
  6. /**
  7. * The stack used to implement dequeue functionality
  8. */
  9. private Stack<Integer> outStack;
  10. /**
  11. * The front element in the stack `inStack` 's queue
  12. */
  13. private int front;
  14. /** Initialize your data structure here. */
  15. public MyQueue2() {
  16. inStack = new Stack<>();
  17. outStack = new Stack<>();
  18. }
  19. /** Push element x to the back of queue. */
  20. public void push(int x) {
  21. if (inStack.empty()) {
  22. front = x;
  23. }
  24. inStack.push(x);
  25. }
  26. /** Removes the element from in front of queue and returns that element. */
  27. public int pop() {
  28. if (empty()) {
  29. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  30. }
  31. if (outStack.isEmpty()) {
  32. while (!inStack.isEmpty()) {
  33. outStack.push(inStack.pop());
  34. }
  35. }
  36. return outStack.pop();
  37. }
  38. /** Get the front element. */
  39. public int peek() {
  40. if (empty()) {
  41. throw new IllegalArgumentException("[ERROR] The queue is empty!");
  42. }
  43. if (!outStack.isEmpty()) {
  44. return outStack.peek();
  45. } else {
  46. return front;
  47. }
  48. }
  49. /** Returns whether the queue is empty. */
  50. public boolean empty() {
  51. return inStack.isEmpty() && outStack.isEmpty();
  52. }
  53. }

Python 实现

  1. class MyQueue:
  2. def __init__(self):
  3. """
  4. Initialize your data structure here.
  5. """
  6. self._in_stack, self._out_stack, self._front = [], [], None
  7. def push(self, x):
  8. """
  9. Push element x to the back of queue.
  10. :type x: int
  11. :rtype: void
  12. """
  13. if not self._in_stack:
  14. self._front = x
  15. self._in_stack.append(x)
  16. def pop(self):
  17. """
  18. Removes the element from in front of queue and returns that element.
  19. :rtype: int
  20. """
  21. if self.empty():
  22. raise Exception("[ERROR] The queue is empty!")
  23. if not self._out_stack:
  24. while self._in_stack:
  25. self._out_stack.append(self._in_stack.pop())
  26. return self._out_stack.pop()
  27. def peek(self):
  28. """
  29. Get the front element.
  30. :rtype: int
  31. """
  32. if self.empty():
  33. raise Exception("[ERROR] The queue is empty!")
  34. if not self._out_stack:
  35. return self._front
  36. else:
  37. return self._out_stack[-1]
  38. def empty(self):
  39. """
  40. Returns whether the queue is empty.
  41. :rtype: bool
  42. """
  43. return not self._in_stack and not self._out_stack

【LeetCode题解】232_用栈实现队列(Implement-Queue-using-Stacks)的更多相关文章

  1. LeetCode 232:用栈实现队列 Implement Queue using Stacks

    题目: 使用栈实现队列的下列操作: push(x) -- 将一个元素放入队列的尾部. pop() -- 从队列首部移除元素. peek() -- 返回队列首部的元素. empty() -- 返回队列是 ...

  2. LeetCode 232. 用栈实现队列(Implement Queue using Stacks) 4

    232. 用栈实现队列 232. Implement Queue using Stacks 题目描述 使用栈实现队列的下列操作: push(x) -- 将一个元素放入队列的尾部. pop() -- 从 ...

  3. [Swift]LeetCode232. 用栈实现队列 | Implement Queue using Stacks

    Implement the following operations of a queue using stacks. push(x) -- Push element x to the back of ...

  4. leetcode:Implement Stack using Queues 与 Implement Queue using Stacks

    一.Implement Stack using Queues Implement the following operations of a stack using queues. push(x) - ...

  5. Leetcode 232 Implement Queue using Stacks 和 231 Power of Two

    1. 232 Implement Queue using Stacks 1.1 问题描写叙述 使用栈模拟实现队列.模拟实现例如以下操作: push(x). 将元素x放入队尾. pop(). 移除队首元 ...

  6. leetcode 155. Min Stack 、232. Implement Queue using Stacks 、225. Implement Stack using Queues

    155. Min Stack class MinStack { public: /** initialize your data structure here. */ MinStack() { } v ...

  7. 【LeetCode】232 & 225 - Implement Queue using Stacks & Implement Stack using Queues

    232 - Implement Queue using Stacks Implement the following operations of a queue using stacks. push( ...

  8. Lintcode: Implement Queue by Stacks 解题报告

    Implement Queue by Stacks 原题链接 : http://lintcode.com/zh-cn/problem/implement-queue-by-stacks/# As th ...

  9. 232. Implement Queue using Stacks,225. Implement Stack using Queues

    232. Implement Queue using Stacks Total Accepted: 27024 Total Submissions: 79793 Difficulty: Easy Im ...

随机推荐

  1. codeforces 877e

    E. Danil and a Part-time Job time limit per test 2 seconds memory limit per test 256 megabytes input ...

  2. HDU3506环形石子合并问题

    HDU3506环形石子合并问题 线性的石子合并问题比较好理解,环形的转成线性的方法就是扩展数组 1 2 3 . . . n 1 2 3 ... n 依据是我们最优的取值可以是 1 --- n也能是 2 ...

  3. iOS处理视图上同时添加单击与双击手势的冲突问题

    _bgView.userInteractionEnabled = YES; //在cell上添加 bgView,给bgView添加两个手势检测方法 UITapGestureRecognizer *do ...

  4. JAVA的初始化顺序

    这里主要是介绍JAVA的类的初始化顺序,比较基础:主要是以例子演示为主: 例子一: 1 package com.cnblog.GDUTtiantian; 2 3 /** 4 * 5 * @author ...

  5. Azure DevOps Server 2019 (TFS)安装教程

    概述 Azure DevOps Server 2019 (之前的名称为TFS),作为微软Azure DevOps 的企业私有(on-premises)服务器,是一个为开发团队提供软件协作开发管理的服务 ...

  6. Sql Server Report 导出到EXCEL 指定行高

    在SQL SERVER REPORT 2005做报表的时候,发现在report中指定的行高没有用.google了一下,找到了解决方法. Make both CanGrow and CanShrink ...

  7. WPF实现窗体中的悬浮按钮

    WPF实现窗体中的悬浮按钮,按钮可拖动,吸附停靠在窗体边缘. 控件XAML代码: <Button x:Class="SunCreate.Common.Controls.FloatBut ...

  8. System.Data.SQLite安装的相关问题

    在使用System.Data.SQLite的过程中,遇到各种问题,特此记录下.(都被搞的折寿了,不仔细看文档的下场!) 1.选对.net Framework的版本. 2.X64和X86的问题,如果项目 ...

  9. [译] 玩转ptrace (一)

    [本文翻译自这里: http://www.linuxjournal.com/article/6100?page=0,0,作者:Pradeep Padaia] 你是否曾经想过怎样才能拦截系统调用?你是否 ...

  10. ceph 源码安装 configure: error: "Can't find boost spirit headers"

    问题:configure: error: "Can't find boost spirit headers" 解决: 推荐:sudo apt-get install libboos ...