设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
对于栈,队列,链表,哈希表这类设计题型,我们需要构造出Node节点去存储相应的值
在链表类题型中,根据链表存储类型可构造以下节点
class Node {
int val;
Node next;
public Node() {}
public Node(int val) {
this.val = val;
}
}
因为只需要存储值和next指针,我们只需构造出只有值和指针的Node类即可
在哈希表中,因为哈希表是根据数组加链表实现的,链表中不仅要存储key值,还需要存储对应的val值,所以我们需要三个属性,key,val,next指针,哈希表中key值不能更改,所以我们使用setter,getter来对key以及val赋值
class Node {
private int key;
private int val;
Node next;
Node() {
}
Node(int key, int val) {
this.key = key;
this.val = val;
}
public int getKey() {
return key;
}
public int getVal() {
return val;
}
public void setVal(int val) {
this.val = val;
}
}
根据题意,我们需要有队列最大长度k,队列当前长度size,虚拟头结点head,使用虚拟头结点记录队列的初始节点。
在本题中,我们主要需要实现的时enQueue(val)以及deQueue(),其他四个方法很容易就可以实现
根据队列特性,插入应插入到队列尾部,弹出应弹出队列头部的Node节点,代码如下,会在代码中解释
public boolean enQueue(int value) {
if (size == k) return false; // 如果队列已满,返回插入失败
Node node; // 定义初始节点,如果队列中没有值直接添加,下面那个while不会走,如果有值,找到尾结点
if (head.next == null) {
node = head;
} else {
node = head.next;
}
while (node.next != head.next) { // 如果当前节点的下一个是头结点,那么当前节点就是尾结点,在当前节点后面添加新节点
node = node.next;
}
Node insert = new Node(value);
node.next = insert;
insert.next = head.next;
size++; // 队列长度加1
return true;
}
public boolean deQueue() {
if (size == 0) return false; // 如果队列为空,返回删除失败
if (head.next.next == head.next) { // 判断是否只有一个节点,如果只有一个节点,直接让虚拟头结点指向null,这里需要特判下,我还没有想出不需要特判的解法,如果哪位小伙伴知道,可以私信我
head.next = null;
} else { // 找到队列尾节点,让尾节点指向头结点的下一个节点,之后改变虚拟头结点指向,在Java中这样就达到了删除的效果
Node node = head.next;
while (node.next != head.next) {
node = node.next;
}
node.next = node.next.next;
head.next = node.next;
}
size--; // 队列长度减1
return true;
}
整体代码
class MyCircularQueue {
class Node {
int val;
Node next;
public Node() {
}
public Node(int val) {
this.val = val;
}
@Override
public String toString() {
return String.valueOf(val);
}
}
Node head;
int k;
int size;
public MyCircularQueue(int k) {
this.k = k;
size = 0;
head = new Node();
}
public boolean enQueue(int value) {
if (size == k) return false;
Node node;
if (head.next == null) {
node = head;
} else {
node = head.next;
}
while (node.next != head.next) {
node = node.next;
}
Node insert = new Node(value);
node.next = insert;
insert.next = head.next;
size++;
return true;
}
public boolean deQueue() {
if (size == 0) return false;
if (head.next.next == head.next) {
head.next = null;
} else {
Node node = head.next;
while (node.next != head.next) {
node = node.next;
}
node.next = node.next.next;
head.next = node.next;
}
size--;
return true;
}
public int Front() {
return head.next == null ? -1 : head.next.val;
}
public int Rear() {
if (size == 0) return -1;
Node node = head.next;
while (node.next != head.next) {
node = node.next;
}
return node.val;
}
public boolean isEmpty() {
return size == 0;
}
public boolean isFull() {
return size == k;
}
}
然后我发现只击败了9.47%的用户,感觉咋都不至于这么低,就去看了下题解
在题解中官方是使用数组+双指针构建的循环队列,所以效率特别快
受队列是特殊的链表影响,所以开始就没想到用数组去解,而是用链表的方式去解