概念

队列也是一种线性结构,相比数组,队列对应的操作是数组的子集,只能从一端(队尾)添加元素,只能从另一端(队首)取出元素.
image.png

特点

队列是一种先进先出的数据结构.

数组队列

实现

  1. public interface Queue<E> {
  2. /**
  3. * 入队操作
  4. *
  5. * 时间复杂度 O(1) 均摊 ArrayQueue 和 LoopQueue同
  6. * @param e 元素
  7. */
  8. void enqueue(E e);
  9. /**
  10. * 出队操作
  11. *
  12. * 时间复杂度 O(n) ArrayQueue复杂度为O(n) LoopQueue复杂度O(1) 均摊
  13. * 出队拿出数组中第一个元素后,剩下的元素都要前移
  14. * @return 元素
  15. */
  16. E dequeue();
  17. /**
  18. * 查看队列队首的元素
  19. *
  20. * 时间复杂度 O(1) ArrayQueue 和 LoopQueue同
  21. * @return 元素
  22. */
  23. E getFront();
  24. /**
  25. * 获取队列的数量
  26. *
  27. * 时间复杂度 O(1) ArrayQueue 和 LoopQueue同
  28. * @return size
  29. */
  30. int getSize();
  31. /**
  32. * 判断队列是否为空
  33. *
  34. * 时间复杂度 O(1) ArrayQueue 和 LoopQueue同
  35. * @return true为空, false反之
  36. */
  37. boolean isEmpty();
  38. }

注意:这里的Array类是前面动态数组实现的Array类,点击跳转:动态数组

  1. public class ArrayQueue<E> implements Queue<E> {
  2. Array<E> array;
  3. public ArrayQueue(int capacity){
  4. array = new Array<>(capacity);
  5. }
  6. public ArrayQueue(){
  7. array = new Array<>();
  8. }
  9. @Override
  10. public void enqueue(E e) {
  11. //向数组的末尾添加元素
  12. array.addLast(e);
  13. }
  14. @Override
  15. public E dequeue() {
  16. //取出第一个元素
  17. return array.removeFirst();
  18. }
  19. @Override
  20. public E getFront() {
  21. return array.getFirst();
  22. }
  23. @Override
  24. public int getSize() {
  25. return array.getSize();
  26. }
  27. @Override
  28. public boolean isEmpty() {
  29. return array.isEmpty();
  30. }
  31. public int getCapacity(){
  32. return array.getCapacity();
  33. }
  34. @Override
  35. public String toString() {
  36. StringBuilder builder = new StringBuilder();
  37. builder.append("Queue: ");
  38. builder.append("front [");
  39. for (int i = 0; i < array.getSize(); i++) {
  40. builder.append(array.get(i));
  41. if (i != array.getSize() - 1) {
  42. builder.append(",");
  43. }
  44. }
  45. builder.append("] tail");
  46. return builder.toString();
  47. }
  48. public static void main(String[] args) {
  49. ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
  50. for (int i = 0; i < 10; i++) {
  51. //向队列中添加元素
  52. arrayQueue.enqueue(i);
  53. System.out.println(arrayQueue);
  54. if (i % 3 == 2){
  55. arrayQueue.dequeue();
  56. System.out.println(arrayQueue);
  57. }
  58. }
  59. }
  60. }

局限

队列的出队操作时间复杂度为O(n),因为出队是把数组的第一个元素删除,那么剩余的其他元素必须全部前移.

循环队列

队首front == 队尾tail 时队列为空
(tail + 1) % 队列长度capacity== front时 队列满

实现

  1. public class LoopQueue<E> implements Queue<E> {
  2. private E[] data;
  3. private int front, tail;
  4. private int size;
  5. @SuppressWarnings("unchecked")
  6. public LoopQueue(int capacity) {
  7. data = (E[]) new Object[capacity + 1];
  8. front = 0;
  9. tail = 0;
  10. size = 0;
  11. }
  12. public int getCapacity() {
  13. return data.length - 1;
  14. }
  15. public LoopQueue() {
  16. this(10);
  17. }
  18. @Override
  19. public void enqueue(E e) {
  20. if ((tail + 1) % data.length == front) {
  21. resize(getCapacity() * 2);
  22. }
  23. data[tail] = e;
  24. tail = (tail + 1) % data.length;
  25. size++;
  26. }
  27. @SuppressWarnings("unchecked")
  28. private void resize(int newCapacity) {
  29. E[] newData = (E[]) new Object[newCapacity + 1];
  30. for (int i = 0; i < size; i++) {
  31. //新数组第一个元素对应的data是front,第二个元素是front+1,第三个是front+2,依次类推
  32. //循环队列可能会产生数组越界
  33. newData[i] = data[(i + front) % data.length];
  34. }
  35. data = newData;
  36. front = 0;
  37. tail = size;
  38. }
  39. @Override
  40. public E dequeue() {
  41. if (isEmpty()) {
  42. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  43. }
  44. E ret = data[front];
  45. data[front] = null;
  46. front = (front + 1) % data.length;
  47. size--;
  48. //缩容
  49. if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
  50. resize(getCapacity() / 2);
  51. }
  52. return ret;
  53. }
  54. @Override
  55. public E getFront() {
  56. if (isEmpty()) {
  57. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  58. }
  59. return data[front];
  60. }
  61. @Override
  62. public int getSize() {
  63. return size;
  64. }
  65. @Override
  66. public boolean isEmpty() {
  67. return front == tail;
  68. }
  69. @Override
  70. public String toString() {
  71. StringBuilder builder = new StringBuilder();
  72. builder.append(String.format("Queue: size = %d,capacity = %d\n", size, data.length));
  73. builder.append("fount [");
  74. for (int i = 0; i != tail; i = (i + 1) % data.length) {
  75. builder.append(data[i]);
  76. if (i != size - 1) {
  77. builder.append(",");
  78. }
  79. }
  80. builder.append("] tail");
  81. return builder.toString();
  82. }
  83. }

数组队列和循环队列比较

  1. public class TestQueue {
  2. public static void main(String[] args) {
  3. int opCount = 100000;
  4. System.out.println("数组队列ArrayQueue耗时: " + testQueue(new ArrayQueue<>(), opCount) + "s");
  5. System.out.println("循环队列LoopQueue耗时: " + testQueue(new LoopQueue<>(), opCount) + "s");
  6. }
  7. private static double testQueue(Queue<Integer> q, int opCount) {
  8. long startTime = System.nanoTime();
  9. SecureRandom random = new SecureRandom();
  10. for (int i = 0; i < opCount; i++) {
  11. //入队操作
  12. q.enqueue(random.nextInt(Integer.MAX_VALUE));
  13. }
  14. for (int i = 0; i < opCount; i++) {
  15. //出队操作
  16. q.dequeue();
  17. }
  18. long endTime = System.nanoTime();
  19. return (endTime - startTime)/1000000000.0;
  20. }
  21. }

双端队列

实现

  1. public class Deque<E> {
  2. private E[] data;
  3. private int front, tail;
  4. private int size;
  5. @SuppressWarnings("unchecked")
  6. public Deque(int capacity) {
  7. data = (E[]) new Object[capacity];
  8. front = 0;
  9. tail = 0;
  10. size = 0;
  11. }
  12. public Deque() {
  13. this(10);
  14. }
  15. public int getCapacity() {
  16. return data.length;
  17. }
  18. public boolean isEmpty() {
  19. return size == 0;
  20. }
  21. public int getSize() {
  22. return size;
  23. }
  24. /**
  25. * 在队尾添加元素
  26. *
  27. * @param e 元素
  28. */
  29. public void addLast(E e) {
  30. // addLast 的逻辑和我们之前实现的队列中的 enqueue 的逻辑是一样的
  31. if (size == getCapacity()) {
  32. resize(getCapacity() * 2);
  33. }
  34. data[tail] = e;
  35. tail = (tail + 1) % data.length;
  36. size++;
  37. }
  38. /**
  39. * 在队首添加元素
  40. *
  41. * @param e 元素
  42. */
  43. public void addFront(E e) {
  44. if (size == getCapacity()) {
  45. resize(getCapacity() * 2);
  46. }
  47. // 我们首先需要确定添加新元素的索引位置
  48. // 这个位置是 front - 1 的地方
  49. // 但是要注意,如果 front == 0,新的位置是 data.length - 1 的位置
  50. front = front == 0 ? data.length - 1 : front - 1;
  51. data[front] = e;
  52. size++;
  53. }
  54. /**
  55. * 删除队首元素
  56. *
  57. * @return 返回被删除的元素
  58. */
  59. public E removeFront() {
  60. // removeFront 的逻辑和我们之前实现的队列中的 dequeue 的逻辑是一样的
  61. if (isEmpty()) {
  62. throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
  63. }
  64. E ret = data[front];
  65. data[front] = null;
  66. front = (front + 1) % data.length;
  67. size--;
  68. if (getSize() == getCapacity() / 4 && getCapacity() / 2 != 0) {
  69. resize(getCapacity() / 2);
  70. }
  71. return ret;
  72. }
  73. /**
  74. * 删除队尾元素
  75. *
  76. * @return 被删除的元素
  77. */
  78. public E removeLast() {
  79. if (isEmpty()) {
  80. throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
  81. }
  82. // 计算删除掉队尾元素以后,新的 tail 位置
  83. tail = tail == 0 ? data.length - 1 : tail - 1;
  84. E ret = data[tail];
  85. data[tail] = null;
  86. size--;
  87. if (getSize() == getCapacity() / 4 && getCapacity() / 2 != 0) {
  88. resize(getCapacity() / 2);
  89. }
  90. return ret;
  91. }
  92. /**
  93. * 获取队首元素
  94. *
  95. * @return 元素
  96. */
  97. public E getFront() {
  98. if (isEmpty()) {
  99. throw new IllegalArgumentException("Queue is empty.");
  100. }
  101. return data[front];
  102. }
  103. /**
  104. * 获取队尾元素
  105. *
  106. * @return 队尾元素
  107. */
  108. public E getLast() {
  109. if (isEmpty()) {
  110. throw new IllegalArgumentException("Queue is empty.");
  111. }
  112. // 因为 tail 指向的是队尾元素的下一个位置,我们需要计算一下真正队尾元素的索引
  113. int index = tail == 0 ? data.length - 1 : tail - 1;
  114. return data[index];
  115. }
  116. /**
  117. * 缩容和扩容
  118. *
  119. * @param newCapacity 新的队列长度
  120. */
  121. @SuppressWarnings("unchecked")
  122. private void resize(int newCapacity) {
  123. E[] newData = (E[]) new Object[newCapacity];
  124. for (int i = 0; i < size; i++) {
  125. newData[i] = data[(i + front) % data.length];
  126. }
  127. data = newData;
  128. front = 0;
  129. tail = size;
  130. }
  131. @Override
  132. public String toString() {
  133. StringBuilder res = new StringBuilder();
  134. res.append(String.format("Queue: size = %d , capacity = %d\n", getSize(), getCapacity()));
  135. res.append("front [");
  136. for (int i = 0; i < size; i++) {
  137. res.append(data[(i + front) % data.length]);
  138. if (i != size - 1) {
  139. res.append(", ");
  140. }
  141. }
  142. res.append("] tail");
  143. return res.toString();
  144. }
  145. public static void main(String[] args) {
  146. // 在下面的双端队列的测试中,偶数从队尾加入;奇数从队首加入
  147. Deque<Integer> dq = new Deque<>();
  148. for (int i = 0; i < 16; i++) {
  149. if (i % 2 == 0) {
  150. dq.addLast(i);
  151. } else {
  152. dq.addFront(i);
  153. }
  154. System.out.println(dq);
  155. }
  156. // 之后,我们依次从队首和队尾轮流删除元素
  157. System.out.println();
  158. for (int i = 0; !dq.isEmpty(); i++) {
  159. if (i % 2 == 0) {
  160. dq.removeFront();
  161. } else {
  162. dq.removeLast();
  163. }
  164. System.out.println(dq);
  165. }
  166. }
  167. }

用队列实现栈(leetcode225)

要求

你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通队列的全部四种操作(push、top、pop 和 empty)
实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

    Java实现

    ```java public class MyStack {

    Queue queue;

    public MyStack() {

    1. queue = new LinkedList<>();

    }

    /**

    • Push element x onto stack. */ public void push(int x) { //队列长度 int size = queue.size(); queue.offer(x); for (int i = 0; i < size; i++) {

      1. //将队列的第一个元素删除并再次放入队列
      2. queue.offer(queue.poll());

      } }

      /**

    • Removes the element on top of the stack and returns that element. */ public int pop() { if (queue.size() == 0) {

      1. throw new IllegalArgumentException("queue is empty");

      } return queue.poll(); }

      /**

    • Get the top element. */ public int top() { if (queue.size() == 0) {

      1. throw new IllegalArgumentException("queue is empty");

      } return queue.peek(); }

      /**

    • Returns whether the stack is empty. */ public boolean empty() { return queue.isEmpty(); }

}

  1. <a name="gR5G8"></a>
  2. ### go实现
  3. ```go
  4. type MyStack struct {
  5. queue []int
  6. }
  7. /** Initialize your data structure here. */
  8. func Constructor() (s MyStack) {
  9. return
  10. }
  11. /** Push element x onto stack. */
  12. func (s *MyStack) Push(x int) {
  13. n := len(s.queue)
  14. s.queue = append(s.queue, x)
  15. for ; n > 0; n-- {
  16. s.queue = append(s.queue, s.queue[0])
  17. s.queue = s.queue[1:]
  18. }
  19. }
  20. /** Removes the element on top of the stack and returns that element. */
  21. func (s *MyStack) Pop() int {
  22. v := s.queue[0]
  23. s.queue = s.queue[1:]
  24. return v
  25. }
  26. /** Get the top element. */
  27. func (s *MyStack) Top() int {
  28. return s.queue[0]
  29. }
  30. /** Returns whether the stack is empty. */
  31. func (s *MyStack) Empty() bool {
  32. return len(s.queue) == 0
  33. }

用栈实现队列(leetcode232)

要求

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty)

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

    Java实现

    ```java public class MyQueue { Deque inStack; Deque outStack;

    /**

    • Initialize your data structure here. */ public MyQueue() { inStack = new LinkedList<>(); outStack = new LinkedList<>(); }

      /**

    • Push element x to the back of queue. */ public void push(int x) { inStack.push(x); }

      /**

    • Removes the element from in front of queue and returns that element. */ public int pop() { //当输出栈为空时 if (outStack.isEmpty()) {

      1. //执行将输入栈中元素依次弹出并放入输出栈
      2. in2out();

      } return outStack.pop(); }

      /**

    • Get the front element. */ public int peek() { if (outStack.isEmpty()) {

      1. in2out();

      } return outStack.peek(); }

      /**

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

      private void in2out() { //当输入栈不为空时,循环将输入栈得元素放入输出栈 while (!inStack.isEmpty()) {

      1. outStack.push(inStack.pop());

      } } }

  1. <a name="2LqTh"></a>
  2. ### go实现
  3. ```go
  4. type MyQueue struct {
  5. inStack, outStack []int
  6. }
  7. func Constructor1() (q MyQueue) {
  8. return
  9. }
  10. func (q *MyQueue) Push(x int) {
  11. q.inStack = append(q.inStack, x)
  12. }
  13. func (q *MyQueue) in2out() {
  14. for len(q.inStack) > 0 {
  15. q.outStack = append(q.outStack, q.inStack[len(q.inStack)-1])
  16. q.inStack = q.inStack[:len(q.inStack)-1]
  17. }
  18. }
  19. func (q *MyQueue) Pop() int {
  20. if len(q.outStack) == 0 {
  21. q.in2out()
  22. }
  23. x := q.outStack[len(q.outStack)-1]
  24. q.outStack = q.outStack[:len(q.outStack)-1]
  25. return x
  26. }
  27. func (q *MyQueue) Peek() int {
  28. if len(q.outStack) == 0 {
  29. q.in2out()
  30. }
  31. return q.outStack[len(q.outStack)-1]
  32. }
  33. func (q *MyQueue) Empty() bool {
  34. return len(q.inStack) == 0 && len(q.outStack) == 0
  35. }

项目demo