今天我们来学习队列🧐
队列是由双向链表LinkedList作为底层的一个接口,为数据结构的一种。下面让我们正式开始队队列的学习吧😎
队列的特点是先进先出,就像排队一样,队尾进队头出

Queue的方法不多,只有以下六种👇:
| 方法名 | 作用 |
| add(E) | 在队尾添加元素 |
| offer(E) | 在队尾添加元素 |
| remove() | 弹出队头元素 |
| poll() | 弹出队头元素 |
| element() | 返回队头元素但不弹出 |
| peek() | 返回队头元素但不弹出 |
细心的同学可能会发现,怎么Queue的这六个方法两个两个都是重复的🧐它们又有什么不同嘞?
其实在平常的使用中,我们常使用的方法有三个:offer(E)、poll()、peek()。那么它们的真正区别是什么呢?
要知道这些相同作用的方法有什么区别,还要从源头——源码查起
在IDEA上,我们通过使用ctrl+鼠标左键点击进入到add和offer的源码,在add和offer的上方我们分别可以看到其注释👇

将add和offer的注释分别翻译后比较👇:


我们可以由上面的图得知,在容量满的时候,offer相较于add的直接抛异常,offer会好一些不会直接暂停程序。
当然,其实选择哪一个方法其实差别都不算太大,作者较喜欢使用的是offer、poll、peek这三个函数😎大家可以自己选择
我们来看下面两行代码👇:
- Queue
qu1 = new LinkedList<>(); - LinkedList
qu2 = new LinkedList<>();
这两行代码虽然后半段都是相同的,但由于创建变量的类型不同,导致qu1的方法会远远少于qu2,这是因为qu1的类型是接口,而Queue接口只有6个方法,要创建实体得利用LinkedList,但是只是相当于LinkedList将Queue中的方法给重写了,而Queue实际只有6个方法的这个事实无法改变。通过在IDEA中试一下就可知道👇:

在解决完这个问题后,我们又想,为什么LinkedList会有这么多方法?🧐
Deque接口的底层是一个双端队列,也就是怎么进出都可。如:队头进,队头出;队头进,队尾出;队尾进,队头出;队尾进,队尾出;
要正式了解一个数据结构,仿写是一个最好的了解方式,下面我们可以自己仿写一个Queue。
若在单向链表的情况下,我们需要考虑一个问题,入队的时候是使用头插还是尾插?
根据头插和尾插入队来分析:
所以头插尾插入队都差不多,要是想要入队出队时间复杂度都为O(1)的话,我们需要设置多一个last节点,专门在最后一个节点的位置处,用于出队,这样就可以让入队出队的时间复杂度都为O(1)了。但我们要知道,真正的单向链表中,是没有last这个成员属性的。
下面开始真正的仿写😎
- public class MyLinkedList {
- public static class Node{
- public int val;
- public Node next;
-
- public Node(){
-
- }
-
- public Node(int val){
- this.val = val;
- }
- }
-
- public Node head;
- public Node last;
-
- //从尾入,从头出
- //尾插法
- public void offer(int val){
- if(head==null){
- head = new Node(val);
- last = head;
- }
- Node cur = new Node(val);
- last.next = cur;
- last = cur;
- }
-
- public int poll(){
- if(head==null||last==null){
- throw new QueueEmptyException("队列为空");
- }
- int val = head.val;
- head = head.next;
- return val;
- }
-
- public int peek(){
- if(head==null||last==null){
- throw new QueueEmptyException("队列为空");
- }
- int val = head.val;
- return val;
- }
-
- public int size(){
- Node cur = head;
- int size = 0;
- while(cur!=null){
- cur = cur.next;
- size++;
- }
- return size;
- }
-
- }
以上是用链表实现,再来看用数组要怎样实现仿写?
数组仿写相对来说较为麻烦,因为数组仿写会有空间浪费的情况出现,原因是当我们出队列的时候front(队头)下标会往后走,从而产生空间浪费。


这是,要让我们的空间不会浪费,就需要用到循环数组👇:

当我们入队列或者出队列的时候,front下标和rear下标都可以跟着,以达到不浪费空间的情况。
在利用循环数组创建列表前,要先理清两个问题:
1️⃣首先解决第一个问题,要判断空或是满,有三种解决方法:①使用flag;②实际大小==数组大小;③将front位置的前一个位置置为空,不放入数据。在后面的代码里我们用第三种方法解决。
2️⃣第二个问题要解决,我们可以使用(rear+1)%数组大小 这条公式就可以解决。
利用循环数组来仿写队列在LeetCode中是有题目的,我们可以通过尝试写这道题目来理解😎下面分别是LeetCode题目链接和博主写的该题目的链接👇
| LeetCode第622题.设计循环队列(原题目) | LeetCode第622题—设计循环队列(博主讲解版本) |
在模仿完了队列后,相信我们会对队列的了解更加深刻😎
在学习完队列后,我们可以开始写题了,下面是博主认为不错的题目,分享给大家,也有博主的题目分析😎
| LeetCode原题 | 博主分析 |
以上!便是全部的啦😎
又是收获满满的一天~