题目
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k): 构造器,设置队列长度为 k 。
Front: 从队首获取元素。如果队列为空,返回 -1 。
Rear: 获取队尾元素。如果队列为空,返回 -1 。
enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
isEmpty(): 检查循环队列是否为空。
isFull(): 检查循环队列是否已满。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/design-circular-queue
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
思路
读题之后得到关键信息,循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环
那么我们只要设计一个收尾相连的队列即可,第一时间就想到了指针,当然JAVA中就是引用了。创建一个循环的引用就可以拥有首位相连的效果
当新增一个节点(入队)的时候,需要在队列的尾部进行插入,打断原本位于尾部的元素和队首元素的关联并重新建立新元素和原本队尾和队首元素的关联
删除一个节点(出队),把队首元素移除,重新维护第二个元素和队尾元素的关联
大体思路有了,那就剩下编码实现了
/**
* 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
*
* 循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
*
* 你的实现应该支持如下操作:
*
* MyCircularQueue(k): 构造器,设置队列长度为 k 。
* Front: 从队首获取元素。如果队列为空,返回 -1 。
* Rear: 获取队尾元素。如果队列为空,返回 -1 。
* enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
* deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
* isEmpty(): 检查循环队列是否为空。
* isFull(): 检查循环队列是否已满。
*
* 来源:力扣(LeetCode)
* 链接:https://leetcode-cn.com/problems/design-circular-queue
* 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
*
* @author tengdj
* @date 2020/5/6 17:41
**/
public class MyCircularQueue {
Node firstNode;
Node lastNode;
int currIndex = 0;
int size = 0;
class Node {
private Node pre;
private Node next;
private int index;
private int value;
public Node(int index) {
this.index = index;
}
public void setNext(Node node) {
this.next = node;
}
public Node getNext() {
return this.next;
}
public void setPre(Node node) {
this.pre = node;
}
public Node getPre() {
return this.pre;
}
public int getIndex() {
return this.index;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
}
/**
* Initialize your data structure here. Set the size of the queue to be k.
*/
public MyCircularQueue(int k) {
if (k < 1) {
throw new IllegalArgumentException("长度必须大于0");
}
size = k;
}
/**
* Insert an element into the circular queue. Return true if the operation is successful.
*/
public boolean enQueue(int value) {
if (currIndex >= size) {
return false;
}
if (value < 0 || value > 1000) {
return false;
}
Node n = new Node(currIndex);
n.setValue(value);
if (currIndex == 0) {
n.setPre(n);
n.setNext(n);
setFirstNode(n);
} else if (currIndex == 1) {
n.setPre(firstNode);
n.setNext(firstNode);
lastNode.setNext(n);
} else {
n.setPre(lastNode);
n.setNext(firstNode);
lastNode.setNext(n);
}
setLastNode(n);
firstNode.setPre(n);
currIndex++;
return true;
}
/**
* Delete an element from the circular queue. Return true if the operation is successful.
*/
public boolean deQueue() {
if (currIndex == 0) {
return false;
}
if (currIndex == 1) {
firstNode = null;
lastNode = null;
} else {
Node pre = firstNode.getPre();
Node next = firstNode.getNext();
pre.setNext(next);
next.setPre(pre);
firstNode = next;
}
currIndex--;
return true;
}
/**
* Get the front item from the queue.
*/
public int Front() {
if (firstNode == null) {
return -1;
}
return firstNode.getValue();
}
/**
* Get the last item from the queue.
*/
public int Rear() {
if (lastNode == null) {
return -1;
}
return lastNode.getValue();
}
/**
* Checks whether the circular queue is empty or not.
*/
public boolean isEmpty() {
return currIndex == 0;
}
/**
* Checks whether the circular queue is full or not.
*/
public boolean isFull() {
return currIndex == size;
}
public Node getFirstNode() {
return firstNode;
}
public void setFirstNode(Node firstNode) {
this.firstNode = firstNode;
}
public Node getLastNode() {
return lastNode;
}
public void setLastNode(Node lastNode) {
this.lastNode = lastNode;
}
public int getCurrIndex() {
return currIndex;
}
public void setCurrIndex(int currIndex) {
this.currIndex = currIndex;
}
public int getSize() {
return size;
}
public void setSize(int size) {
this.size = size;
}
}
上面的代码其实是一个环形的双向链表,内存消耗可能会比较大(每个节点都需要存放双向的引用),但是效率还算不错(对象本身维护了队首和队尾元素)
做的时候其实还想过用数组实现,数组其实是最简单的,只要维护好数组当前的也能高效的实现这个需求,但是数组在删除元素的时候需要重新编排索引(数组拷贝)会比较耗时