🔒文章目录:
1.❤️❤️前言~🥳🎉🎉🎉
Hello, Hello~ 亲爱的朋友们👋👋,这里是E绵绵呀✍️✍️。
如果你喜欢这篇文章,请别吝啬你的点赞❤️❤️和收藏📖📖。如果你对我的内容感兴趣,记得关注我👀👀以便不错过每一篇精彩。
当然,如果在阅读中发现任何问题或疑问,我非常欢迎你在评论区留言指正🗨️🗨️。让我们共同努力,一起进步!
加油,一起CHIN UP!💪💪
🔗个人主页:E绵绵的博客
📚所属专栏:1. JAVA知识点专栏
深入探索JAVA的核心概念与技术细节
2.JAVA题目练习
实战演练,巩固JAVA编程技能
3.c语言知识点专栏
揭示c语言的底层逻辑与高级特性
4.c语言题目练习
挑战自我,提升c语言编程能力
📘 持续更新中,敬请期待❤️❤️
2. 队列(Queue)
2.1队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头 (Head/Front)
2.2队列的方法
常用的方法为以上三个方法,但总共有六个方法。
🍓入队列:add()、offer()
相同:未超出容量,从队尾压入元素,返回压入的那个元素。
区别:在超出容量时,add()方法会对抛出异常,offer()返回false
🍓出队列:remove()、poll()相同:容量大于0的时候,删除并返回队头被删除的那个元素。
区别:在容量为0的时候,remove()会抛出异常,poll()返回null
🍓获取队头元素(不删除):element()、peek()相同:容量大于0的时候,都返回队头元素。但是不删除。
区别:容量为0的时候,element()会抛出异常,peek()返回null。虽然有六个方法,但我们经常用的是 offer(),poll(),peek()。知道这另外三个方法就行了
此外我们还需记住size()和isEmpty(),这两个方法之前就见过,想必不用多说了。
2.3队列的使用
由于队列是接口,所以我们不能实例化Queue,要用Queue去接收实现了Queue接口的实例化对象。
队列可以使用顺序表或链表的结构来实现:
当用链表结构来实现时,我们用LinkedList去实例化对象,再用Queue去接收。(LinkedList实现了Queue接口,上图有出现其关系)
当用顺序表结构来实现时,我们用ArrayDeque去实例化对象,再用Queue去接收。
(ArrayDeque实现了Queue接口)
当用链表结构实现队列时,其使用代码如下:
public class Test1 {
public static void main(String[] args) {
Queue<Integer> queue=new LinkedList<>();
queue.offer(4);
queue.offer(3);
queue.offer(2);
queue.offer(1);
System.out.println(queue.peek());
queue.poll();
queue.poll();
System.out.println(queue);
}
}
当用顺序表结构实现队列时,其使用代码如下:
public class Test2 {
public static void main(String[] args) {
Queue<Integer> queue=new ArrayDeque<>();
queue.offer(4);
queue.offer(3);
queue.offer(2);
queue.offer(1);
System.out.println(queue.peek());
queue.poll();
queue.poll();
System.out.println(queue);
}
}
注意:我们用println()打印Queue对象时能以字符串的形式打印出其内部的所有成员值。
2.4循环队列
循环队列的介绍
当我们使用顺序表实现队列时,会存在一个问题:虽然顺序表实现的队列可以自动扩容,但其空间利用不充分:因为每次出队都会浪费一个空间,为了解决这个问题,我们可以采用循环队列。
循环队列是一个特殊的队列:其底层还是数组,但不能实现自动扩容。入队时能够重新从数组的尾部跳到数组头部对已经出队的空间重新利用,这样就能够保证数组的每一个空间都可以被利用。
循环队列图
如果将队列看做是一个循环,那么就可以看做是将数据存储在一个圆环里。
那现在有一个问题,当队列(数组)满的时候,font = rear ,而空的时候也是font=rear。那该怎么判断呢?
如何区分循环队列是满还是空
1、定义一个变量size:记录队列元素个数。
每存放(入队)一个元素size++,每删除(出队)一个元素size--。
当size的值与顺序表的大小相等时,表示队列已满。
size值为0表示队列为空。
2、使用一个boolean类型的成员变量flag进行标记,初始值为false。每次入队时将flag的值置为true,出队将flag的值置为false,
当rear == front && flag == true表示队列已满。
当rear == front && flag == false表示队列为空。
3、浪费一个空间。每次存放元素之前都先检查一下rear的下一个下标与 front 是否相等(也可以使用格式进行判断:(rear+1)% array.length 是否与 front 相等)
如果rear的下一个下标与 front 相等则表示队列已满。
如果rear == front则表示队列为空。这样就导致其中必有一个空间存放不了值,相当于浪费了一个空间去使队列为空的标志和队列已满的标志有区别,从而使其能够判断出来。
数组下标循环技巧
同理在出队时,front 也会出现这种情况,所以使用方式:front = (front+1)%elem.length;
模拟实现循环队列
📌题目描述:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
📋题目示例 :
MyCircularQueue circularQueue = new MyCircularQueue(3); // 设置长度为 3
circularQueue.enQueue(1); // 返回 true
circularQueue.enQueue(2); // 返回 true
circularQueue.enQueue(3); // 返回 true
circularQueue.enQueue(4); // 返回 false,队列已满
circularQueue.Rear(); // 返回 3
circularQueue.isFull(); // 返回 true
circularQueue.deQueue(); // 返回 true
circularQueue.enQueue(4); // 返回 true
circularQueue.Rear(); // 返回 4
💥注意:
代码示例:
class MyCircularQueue {
Integer[] elements;
int font;
int rear;
public MyCircularQueue(int k) {
elements =new Integer[k+1];
}
public boolean enQueue(int value) {
if(isFull())
return false;
else {
elements[rear] = value;
rear = (rear + 1) % elements.length;
return true;
}
}
public boolean deQueue() {
if(isEmpty())
return false;
else{
elements[font]=null;
font=(font+1)%elements.length;
return true;
}
}
public int Front() {
if (isEmpty())
return -1;
else
return elements[font];
}
public int Rear() {
if(isEmpty())
return -1;
else{
if(rear==0)
return elements[elements.length-1];
return elements[rear-1];
}
}
public boolean isEmpty() {
if(rear==font)
return true;
else
return false;
}
public boolean isFull() {
if((rear+1)%elements.length==font)
return true;
else
return false;
}
}
该题链接:循环队列的模拟实现
3.双端队列(Deque)
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
将队列的两端分别称为前端和后端,两端都可以入队和出队。所以双端队列既能够当队列使用,也能当栈使用。(重点这句话)
Java底层是使用LinkedList或ArrayDeque来实现双端队列(Deque)的,前面讲过,队列(Queue)也是使用LinkedList或ArrayDeque来实现的。(使用LinkedList是用链式结构去实现双端队列,使用ArrayDeque是用顺序结构去实现双端队列)
对于Deque的方法,我们常见的依旧是offerFirst,offerLast,pollFirst,pollLast,peekFirst,peekLast。其他的六个方法知道就行。
除此之外我们也还需记住size()和isEmpty()。
当用链表结构实现双端队列时,其使用代码如下:
public class Test3 {
public static void main(String[] args) {
Deque<Integer> deque= new LinkedList<>();
deque.offerLast(4);
deque.offerFirst(3);
deque.offerFirst(5);
deque.offerLast(6);
System.out.println(deque.peekLast());//6
System.out.println(deque.peekFirst());//5
deque.pollLast();
deque.pollFirst();
System.out.println(deque.peekLast());//4
System.out.println(deque.peekFirst());//3
}
}
当用顺序表结构实现双端队列时,其使用代码如下:
public class Test4 {
public static void main(String[] args) {
Deque<Integer> deque= new ArrayDeque<>();
deque.offerLast(4);
deque.offerFirst(3);
deque.offerFirst(5);
deque.offerLast(6);
System.out.println(deque.peekLast());//6
System.out.println(deque.peekFirst());//5
deque.pollLast();
deque.pollFirst();
System.out.println(deque.peekLast());//4
System.out.println(deque.peekFirst());//3
}
}
4.总结
所以这篇文章我们就把队列的概念讲完了,下篇文章将带来队列和栈的习题练习。在此,我们诚挚地邀请各位大佬们为我们点赞、关注,并在评论区留下您宝贵的意见与建议。让我们共同学习,共同进步,为知识的海洋增添更多宝贵的财富!🎉🎉🎉❤️❤️💕💕🥳👏👏👏