一、为什么需要循环队列
1.1 简单队列实现方法(单一头指针方法)
#include <iostream>
class MyQueue {
private:
// store elements
vector<int> data;
// a pointer to indicate the start position
int p_start;
public:
MyQueue() {p_start = 0;}
/** Insert an element into the queue. Return true if the operation is successful. */
bool enQueue(int x) {
data.push_back(x);
return true;
}
/** Delete an element from the queue. Return true if the operation is successful. */
bool deQueue() {
if (isEmpty()) {
return false;
}
p_start++;
return true;
};
/** Get the front item from the queue. */
int Front() {
return data[p_start];
};
/** Checks whether the queue is empty or not. */
bool isEmpty() {
return p_start >= data.size();
}
};
int main() {
MyQueue q;
q.enQueue(5);
q.enQueue(3);
if (!q.isEmpty()) {
cout << q.Front() << endl;
}
q.deQueue();
if (!q.isEmpty()) {
cout << q.Front() << endl;
}
q.deQueue();
if (!q.isEmpty()) {
cout << q.Front() << endl;
}
}
1.2 缺点
上面的实现很简单,但在某些情况下效率很低。 随着起始指针的移动,浪费了越来越多的空间。 当我们有空间限制时,这将是难以接受的。一旦队列满了,我们将无法再添加更多的元素。即使我们删除了一个元素,我们也不能再次添加新的元素。只有当删除完所有元素及清空所有元素后才能重新添加元素。这样的队列效率是非常低的。即使删除了第一个元素也不能在插入多余元素! 只有在整个队列清空后才能再次使用。
二、循环队列
2.1 循环队列详解
-
循环队列的head指针将永远指向队列的最前端,tail指针将永远指向队列的最尾部。初始化时,头指针和尾指针将指向同一个位置,这意味着队列为空。 (Head为头指针,Tail为尾指针)
-
当加入新数据的时候,tail指针将后移
-
循环队列中,其实数据并不一定真的被删除。当头指针加一往后移的时候,D1其实已经不是循环队列中的元素了。所以队列中的元素是Head指针和tial指针中间的数据。
-
当为指针达到最末端的时候将会重新变为0。操作实现关键取余运算!!
-
某些情况下,头指针和尾指针将会交叉。就是头指针可能会比尾指针还要大。例如下状况:当队列满了之后多次删除元素再添加元素,那么此时头指针的位置将大于尾指针。
2.2 循环队列关键点
保持尾指针和头指针在我们所定义的最大队列大小当中是整个循环队列的关键点。我们可已使用取余操作保证头指针和尾指针在最大值之间循环。例:我们假设取最大值为5
head = (head+1) % maxSize
tail = (tail+1) % maxSize
2.3 循环队列实现中的注意事项
在实现删除元素 bool deQueue() 的操作过程中一定要对只有一个元素的状况加以考虑!!
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if (isEmpty()) {
cout << "Nothing to be delete! The queue is empty !" << endl;
return false;
}
else {
if (head == tail) {
//only one element in queue,reset queue after removal
cout << "Delete value: " << data[head] <<" one element "<< endl;
head = -1;
tail = -1;
return true;
}
else {
cout << "Delete value: " << data[head] << endl;
data[head] = -1; //ressetting data that we remove to -1
head = (head + 1) % size;
return true;
}
}
}
2.4 完整代码
class MyCircularQueue {
private:
int head; //the beginning or front of the queue
int tail; //the end or back of the queue
int size; //the size of the queue
std::vector<int> data; //the container to store the data
public:
/** Initialize your data structure here. Set the size of the queue to be k. */
MyCircularQueue(int k) {
head = -1;
tail = -1;
size = k;
data.resize(size);
cout << "Circular Queue Initiated" << " " << "The size is: " << size << endl;
}
/** Insert an element into the circular queue. Return true if the operation is successful. */
bool enQueue(int value) {
if (isFull()) {
//the queue is full
cout << "The queue is full--enQueue" << endl;
return false;
}
else {
tail = (tail + 1) % size;
data[tail] = value;
cout << "enter value is: " << value << endl;
//remember to see if the queue is empty
if (head == -1)
head = 0;
return true;
}
}
/** Delete an element from the circular queue. Return true if the operation is successful. */
bool deQueue() {
if (isEmpty()) {
cout << "Nothing to be delete! The queue is empty !" << endl;
return false;
}
else {
if (head == tail) {
//only one element in queue,reset queue after removal
cout << "Delete value: " << data[head] <<" one element "<< endl;
head = -1;
tail = -1;
return true;
}
else {
cout << "Delete value: " << data[head] << endl;
data[head] = -1; //ressetting data that we remove to -1
head = (head + 1) % size;
return true;
}
}
}
/** Get the front item from the queue. */
int Front() {
if (isEmpty()) {
cout << "The queue is empty!--front" << endl;
return -1;
}
else {
cout << "The front of the queue is: " << data[head] << endl;
return data[head];
}
}
/** Get the last item from the queue. */
int Rear() {
if (isEmpty()) {
cout << "Queue is empty!" << endl;
return -1;
}
else {
cout << "The rear of the queue is: " << data[tail] << endl;
return data[tail];
}
}
/** Checks whether the circular queue is empty or not. */
bool isEmpty() {
if (head == -1 ) {
cout << "The queue is empty!";
return true;
}
else {
cout << "Not empty!" << endl;
return false;
}
}
/** Checks whether the circular queue is full or not. */
bool isFull() {
if ((head==0&&tail==size-1)||(head==tail+1)) {
cout << "The queue is full" << endl;
return true;
}
else
return false;
}
};
三、参考链接
https://leetcode-cn.com/explore/learn/card/queue-stack/216/queue-first-in-first-out-data-structure/863/ LeetCode题目出处
https://medium.com/@jhowerin/circular-queues-in-c-5dc25db1722b 特别注意,该博主的代码其实考虑并不完善,就是在删除元素的过程中漏了一个元素时的状况。 在LeetCode上是不能通过所有的测试集的!!
我在LeetCode过程中遇到不过的测试集:(只要考虑清楚一个元素的时候就可以解决)