难度:中等
题目:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 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
提示:
- 所有的值都在 0 至 1000 的范围内;
- 操作数将在 1 至 1000 的范围内;
- 请不要使用内置的队列库。
Related Topics
- 设计
- 队列
- 数组
- 链表
重点!!!解题思路———链表解法或数组解法
一:数组解法
第一步:
定义两个用来标记循环队列头尾指针的,一个为head,一个为tail
我们让tail始终处于尾部的下一个位置
第二步:
定义一个计数器cnt用来记录目前队列的大小
第三步—思考:
因为是循环队列,当出队时,让head+1,这时如果我们入队时,tail位于数组的最后一个节点,那么我们需要让tail回到队列的头部节点,因为我们的tail节点永远在最后一个节点的下一个节点,即这里需要让(tail+1)%数组长度,才能实现队列的循环。
head同理,如果head位于队列的最后一个位置,当head+1时,我们也需要让head回到队列的头部,即(head+1)%数组长度 。
第四步—思考:
当我们要返回队列的尾部值时,我们需要让tail-1,如果这时候tail位于队列头部,tail-1<0
此时肯定是不行的,所以我们这时tail=(tail-1+数组长度)%数组长度,让tail的下标回到队列的最后一个位置
源码:
class MyCircularQueue {
int[] arr;
int cnt,head,tail;
public MyCircularQueue(int k) { //队列的初始化,让数组长度=给定的长度
arr=new int[k];
head=0;
tail=0;
cnt=0;
}
public boolean enQueue(int value) {
if(isFull()) return false;
arr[tail]=value; //让tail指针位置为添加的元素
tail=(tail+1)%arr.length; //避免队列假溢出
cnt++;
return true;
}
public boolean deQueue() {
if(isEmpty()) return false;
head=(head+1)%arr.length; //避免假溢出
cnt--;
return true;
}
public int Front() {
if(isEmpty()) return -1;
return arr[head];
}
public int Rear() {
if (isEmpty()) return -1;
return arr[(tail-1+arr.length)%arr.length]; //tail-1时 避免为负数
}
public boolean isEmpty() {
return cnt==0;
}
public boolean isFull() {
return cnt==arr.length;
}
}
运行结果:
二:链表解法
第一步:
定义一个内部类class Node,用链表实现循环队列,也是需要两个指针,分别指向队列的头部和队列的尾部,(但是我们这里尾部不是最后一个节点+1 而是真正的尾部)。
第二步:
当向队列添加值时,首先判断目前队列大小是不是0,如果是0的话,那么我们让头节点fount,尾节点rear相等并且等于新添加的node节点。
如果不是0的话,我们让rear的下一个节点为新节点node,然后我们让rear节点向后走一步
第三步:
当出队操作时,我们让fount节点向后走一步,这样fount前面的节点就找不到了,做到了出队的删除操作
源码:
class MyCircularQueue {
class Node{
int val;
Node next;
public Node(int val){
this.val=val;
this.next=null;
}
}
Node front,rear; //定义头部节点 , 尾部节点
int count; //定义一个计数器
int max; //max为队列的长度
public MyCircularQueue(int k) { //进行队列初始化
count=0;
max=k;
}
public boolean enQueue(int value) {
if(isFull()) return false;
Node node = new Node(value);
if(count==0){
front=rear=node;
}else {
rear.next=node;
rear=rear.next;
}
count++;
return true;
}
public boolean deQueue() {
if (isEmpty()) return false;
front=front.next;
count--;
return true;
}
public int Front() {
if (isEmpty()) return -1;
return front.val;
}
public int Rear() {
if (isEmpty()) return -1;
return rear.val;
}
public boolean isEmpty() {
return count==0;
}
public boolean isFull() {
return count==max;
}
}
运行结果:
如果您还有什么疑问或解答有问题,可在下方评论,我会及时回复。
系列持续更新中,点个订阅吧