1.  内容大纲
本章节的代码:https://github.com/wicksonZhang/data-structure/tree/main/4-Queue 
 
 
2.  基础知识2.1.  什么是队列队列:是一种线性表的数据结构,队列有头尾两端,分别是队头和队尾。元素会先从对头进行入队,再从队尾进行出队。遵循 First In First Our 原则。
2.2.  队列的优缺点优点 
开发简单: 队列主要是基于链表或者动态数组,所以开发简单易懂。 
FIFO原则: 队列遵循先进先出原则,保证了数据处理的顺序性和稳定性。 
代码解耦: 例如我们平时常见的消息队列其实底层采用的数据结构就是队列,而消息队列的最大一个特点就是解耦。 
 
缺点 
不支持随机访问: 由于队列基于链表,所以也导致了队列不支持随机访问。 
 
2.3.  队列的分类
普通队列(Basic Queue):  这是最基本的队列形式,按照先进先出(FIFO)的原则管理数据。 
双端队列(Deque):  双端队列支持在队列的两端进行插入和删除操作。可以从前端或后端插入/删除元素,具有更灵活的操作特性。 
循环队列(Circular Queue):  循环队列是一种特殊类型的队列,在队列的基础上做了优化,通过循环利用数组空间来避免数组元素移动的开销。 
 
2.4.  生活中对应的场景
排队场景:例如我们平时排队,遵循的就是 FIFO 原则。 
水管:我们平时用的水管也是遵循 FIFO 原则。 
 
 
3.  普通队列3.1.  接口设计
我们的普通队列主要是基于链表进行实现,所以还是要基于以前的代码。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public  class  Queue <E> {    private  List<E> list = new  LinkedList <>();          public  int  size () {}          public  boolean  isEmpty () {}          public  void  enQueue (E element) {}          public  E deQueue () {}          public  E front () {} } 
 
3.2.  代码实现3.2.1.  初始化成员变量1 private  List<E> list = new  LinkedList <>();
 
3.2.2.  元素的数量1 2 3 4 5 6 7 8 public  int  size ()  {    return  list.size(); } 
 
3.2.3.  是否为空1 2 3 4 5 6 7 8 public  boolean  isEmpty ()  {    return  list.isEmpty(); } 
 
3.2.4.  清空元素1 2 3 4 5 6 public  void  clear ()  {    list.clear(); } 
 
3.2.5.  入队
1 2 3 4 5 6 7 8 public  void  enQueue (E element)  {    list.add(element); } 
 
3.2.6.  出队
1 2 3 4 5 6 7 8 public  E deQueue ()  {    return  list.remove(0 ); } 
 
3.2.7.  获取队列的头元素1 2 3 4 5 6 7 8 public  E front ()  {    return  list.get(0 ); } 
 
3.3.  单元测试1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public  class  BaseQueueTest  {    private  BaseQueue<Integer> queue;     @BeforeEach      public  void  setup ()  {         queue = new  BaseQueue <>();     }     @Test      public  void  testEnqueueAndSize ()  {         queue.enQueue(5 );         queue.enQueue(10 );         Assertions.assertEquals(2 , queue.size());     }     @Test      public  void  testDequeueAndFront ()  {         queue.enQueue(5 );         queue.enQueue(10 );         Assertions.assertEquals(5 , queue.front());         Assertions.assertEquals(5 , queue.deQueue());         Assertions.assertEquals(1 , queue.size());         Assertions.assertEquals(10 , queue.front());     }     @Test      public  void  testIsEmptyAndClear ()  {         Assertions.assertTrue(queue.isEmpty());         queue.enQueue(5 );         queue.enQueue(10 );         Assertions.assertFalse(queue.isEmpty());         queue.clear();         Assertions.assertTrue(queue.isEmpty());     }      } 
 
3.4.  实战练习3.4.1.  用栈实现队列
232. 用栈实现队列 - 力扣(LeetCode) 
 
需求 
实现思路 
首先,准备两个栈。inStack、outStack 
然后,入队时,将元素放入 inStack 栈中。 
最后,出队(pop) 和获取栈顶元素(peek) 时,需要考虑如下两个问题:
outStack 为空:直接将 inStack 栈中的所有元素 push 到 outStack ,然后再从 outStack 弹出。 
outStack 不为空:直接将 outStack 中的元素弹出。 
 
 
 
实现代码 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 class  MyQueue  {    private  Stack<Integer> inStack;     private  Stack<Integer> outStack;     public  MyQueue ()  {         inStack = new  Stack <>();         outStack = new  Stack <>();     }     public  void  push (Integer x)  {         inStack.push(x);     }     public  int  pop ()  {         if  (outStack.isEmpty()) {             while  (!inStack.isEmpty()) {                 outStack.push(inStack.pop());             }         }         return  outStack.pop();     }     public  int  peek ()  {         if  (outStack.isEmpty()) {             while  (!inStack.isEmpty()) {                 outStack.push(inStack.pop());             }         }         return  outStack.peek();     }     public  boolean  empty ()  {         return  inStack.isEmpty() && outStack.isEmpty();     } } 
 
 
4.  双端队列
双端队列本质上也是队列的一种,本质意思就是元素可以从队头和队尾进行入队和出队。
 
4.1.  接口设计
我们的普通队列主要是基于链表进行实现,所以还是要基于以前的代码。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public  class  DeQueue <E> {    private  LinkedList<E> linkedList = new  LinkedList <>();          public  int  size ()  {}          public  boolean  isEmpty ()  {}          public  void  enQueueRear (E element)  {}          public  E deQueueRear ()  {}          public  void  enQueueFront (E element)  {}          public  E deQueueFront ()  {}          public  E front ()  {}          public  E rear ()  {}      } 
 
4.2.  代码实现4.2.1.  初始化成员变量1 private  List<E> linkedList = new  LinkedList <>();
 
4.2.2.  元素的数量1 2 3 4 5 6 7 public  int  size ()  {    return  linkedList.size(); } 
 
4.2.3.  是否为空1 2 3 4 5 6 7 public  boolean  isEmpty ()  {    return  linkedList.isEmpty(); } 
 
4.2.4.  从队尾入队1 2 3 4 5 6 7 8 public  E deQueueRear ()  {    return  linkedList.remove(size() - 1 ); } 
 
4.2.5.  从队头入队1 2 3 4 5 6 7 8 public  E deQueueFront ()  {    return  linkedList.remove(0 ); } 
 
4.2.6.  获取队列的头元素1 2 3 4 5 6 7 8 public  E front ()  {    return  linkedList.get(0 ); } 
 
4.2.7.  获取队列的尾元素1 2 3 4 5 6 7 8 public  E rear ()  {    return  linkedList.get(size() - 1 ); } 
 
4.3.  单元测试1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public  class  DeQueueTest  {    @Test      public  void  testEmptyQueue ()  {         DeQueue<Integer> queue = new  DeQueue <>();                  assertEquals(0 , queue.size());         assertTrue(queue.isEmpty());                           assertThrows(IndexOutOfBoundsException.class, queue::deQueueFront);         assertThrows(IndexOutOfBoundsException.class, queue::deQueueRear);     }     @Test      public  void  testEnqueueDequeue ()  {         DeQueue<String> queue = new  DeQueue <>();         queue.enQueueRear("First" );         queue.enQueueFront("New First" );         assertEquals(2 , queue.size());         assertFalse(queue.isEmpty());         assertEquals("New First" , queue.deQueueFront());         assertEquals("First" , queue.deQueueRear());         assertTrue(queue.isEmpty());     }     @Test      public  void  testFrontRear ()  {         DeQueue<Character> queue = new  DeQueue <>();         queue.enQueueRear('A' );         queue.enQueueRear('B' );         queue.enQueueFront('C' );         queue.enQueueFront('D' );         assertEquals('D' , queue.front());         assertEquals('B' , queue.rear());     }     @Test      public  void  testBoundaryConditions ()  {         DeQueue<Integer> queue = new  DeQueue <>();                  queue.enQueueRear(42 );         assertEquals(42 , (int ) queue.deQueueFront());         assertTrue(queue.isEmpty());                  for  (int  i  =  0 ; i < 1000 ; i++) {             queue.enQueueRear(i);         }         for  (int  i  =  0 ; i < 1000 ; i++) {             assertEquals(i, (int ) queue.deQueueFront());         }         assertTrue(queue.isEmpty());     } } 
 
 
5.  循环队列
循环队列 :就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。
底层可以通过数组进行扩容,实现数据的入队和出队,元素从 rear 指向的位置入队,front 指向的位置出队。
 
5.1.  接口设计
目前我们通过动态数组实现循环队列
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public  class  CircleQueue <E> {         private  int  front;               private  int  size;               private  E[] elements;               public  int  size () {}          public  boolean  isEmpty () {}          public  void  enQueue (E element) {}          public  E deQueue () {}          public  E front () {} } 
 
5.2.  代码实现5.2.1.  初始化成员变量1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @SuppressWarnings("unchecked") public  class  CircleQueue <E> {         private  int  size;          private  int  front;          private  E[] elements;          private  static  final  int  DEFAULT_CAPACITY  =  10 ;          public  CircleQueue ()  {         this .elements = (E[]) new  Object [DEFAULT_CAPACITY];     } } 
 
5.2.2.  元素的数量
与数组保持一致
 
1 2 3 4 5 6 7 8 public  int  size ()  {    return  size; } 
 
5.2.3.  队列是否为空1 2 3 4 5 6 7 8 public  boolean  isEmpty ()  {    return  size == 0 ; } 
 
5.2.4.  入队
入队:入队的其实就是往最后一个元素添加元素,由于我们时循环链表,需要考虑的问题就比较多了。
 
问题一:如果是在最后一个位置进行入队,那么元素应该需要添加到队列的第一个元素。
问题二:由于动态数组的默认容量是 10 , 当队列满了的时候就需要考虑扩容了。
1 2 3 4 5 6 7 8 9 10 11 12 public  void  enQueue (E element)  {         ensureCapacity(size + 1 );     elements[index(size)] = element;     size++; }  
 
5.2.4.1.  获取下一个元素的索引
这个是对动态数组到了最后一个元素时,在添加元素时就需要获取当前元素的索引
 
1 2 3 4 5 6 7 8 9 10 11 12 private  int  index (int  index)  {              index += front;     return  index - (elements.length > index ? 0  : elements.length); } 
 
5.2.4.2.  数组扩容
当数组的容量满了之后就需要进行扩容。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @SuppressWarnings("unchecked") private  void  ensureCapacity (int  capacity)  {    if  (capacity - elements.length > 0 ) {         int  newCapacity  =  capacity + (capacity >> 1 );         E[] newElement = (E[]) new  Object [newCapacity];         for  (int  i  =  0 ; i < size; i++) {             newElement[i] = elements[index(i)];         }         elements = newElement;         front = 0 ;     } } 
 
5.2.5.  出队
出队是比较简单的,我们只需要将 front 索引指向的元素弹出即可。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  E deQueue ()  {    if  (size <= 0 ) {         throw  new  IndexOutOfBoundsException ("This Circle is null" );     }     E  element  =  elements[front];     elements[front] = null ;     front = index(1 );     size--;     return  element; } 
 
5.2.6.  获取队列的头元素
front 指向的元素就是头元素
 
1 2 3 4 5 6 7 8 9 public  E front ()  {    if  (size <= 0 ) {         throw  new  IndexOutOfBoundsException ("This Circle is null" );     }     return  elements[front]; } 
 
5.3.  单元测试1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public  class  CircleQueueTest  {    @Test      public  void  testEmptyQueue ()  {         CircleQueue<Integer> queue = new  CircleQueue <>();                  assertTrue(queue.isEmpty());         assertEquals(0 , queue.size());                  assertThrows(IndexOutOfBoundsException.class, queue::deQueue);         assertThrows(IndexOutOfBoundsException.class, queue::front);         System.out.println("queue = "  + queue);     }     @Test      public  void  testEnqueueAndDequeue ()  {         CircleQueue<String> queue = new  CircleQueue <>();         queue.enQueue("One" );         queue.enQueue("Two" );         queue.enQueue("Three" );         assertEquals(3 , queue.size());         assertFalse(queue.isEmpty());         assertEquals("One" , queue.front());         assertEquals("One" , queue.deQueue());         assertEquals("Two" , queue.deQueue());         assertEquals("Three" , queue.deQueue());         assertTrue(queue.isEmpty());         assertEquals(0 , queue.size());         assertThrows(IndexOutOfBoundsException.class, queue::deQueue);         assertThrows(IndexOutOfBoundsException.class, queue::front);         System.out.println("queue = "  + queue);     }     @Test      public  void  testQueueResize ()  {         CircleQueue<Integer> queue = new  CircleQueue <>();         int  initialCapacity  =  20 ;         for  (int  i  =  0 ; i < initialCapacity; i++) {             queue.enQueue(i);         }         System.out.println("queue = "  + queue);         assertEquals(initialCapacity, queue.size());         for  (int  i  =  0 ; i < initialCapacity; i++) {             assertEquals(i, queue.deQueue());         }         System.out.println("queue = "  + queue);         assertTrue(queue.isEmpty());         assertEquals(0 , queue.size());     }      } 
 
 
6.  参考博文