什么是循环队列?
在计算机科学中,循环队列(Circular Queue)是一种在数组中实现队列的数据结构。其特殊之处在于队列的头部和尾部相接,形成一个环状。它的优点是可以充分利用数组空间,不必浪费存储空间。同时,循环队列也可以通过指定初始位置来实现动态扩容。循环队列的一个典型应用场景是快速处理连续流数据,例如网络数据包、IO事件等。
循环队列的基本要素
头指针和尾指针
头指针和尾指针用于指示队首和队尾元素的位置。为了方便地实现循环队列,在头指针指向的数组位置上不存储数据元素。
class CircularQueue {
private:
int* data_;
int head_;
int tail_;
public:
CircularQueue(int capacity) {
data_ = new int[capacity + 1];
head_ = 0;
tail_ = 0;
}
};
队列容量和队列长度
循环队列的容量是指它能够存储的最大元素数量。循环队列的长度是指它当前存储的元素数量。为了便于区分队空和队满的情况,一般将循环队列的容量设置为实际可用空间减1。
class CircularQueue {
private:
int* data_;
int head_;
int tail_;
int capacity_;
public:
CircularQueue(int capacity) {
data_ = new int[capacity + 1];
head_ = 0;
tail_ = 0;
capacity_ = capacity;
}
int capacity() const { return capacity_; }
int size() const { /* todo */ }
bool empty() const { /* todo */ }
bool full() const { /* todo */ }
};
循环队列的操作
入队操作
入队操作会先判断队列是否已满,如果已满则返回false,否则将新元素插入到尾指针位置,并将尾指针向后移动一位。
class CircularQueue {
public:
bool enqueue(int value) {
if (full()) return false;
data_[tail_] = value;
tail_ = (tail_ + 1) % (capacity_ + 1);
return true;
}
};
出队操作
出队操作会先判断队列是否为空,如果为空则返回false,否则将头指针向后移动一位,并返回对应的元素。
class CircularQueue {
public:
bool dequeue() {
if (empty()) return false;
head_ = (head_ + 1) % (capacity_ + 1);
return true;
}
int front() const { /* todo */ }
int rear() const { /* todo */ }
};
获取队头和队尾元素
获取队头和队尾元素分别需要检查队列是否为空。如果队列为空则返回-1,否则直接返回头指针和尾指针位置上的元素。
class CircularQueue {
public:
int front() const {
if (empty()) return -1;
return data_[head_];
}
int rear() const {
if (empty()) return -1;
return data_[(tail_ - 1 + capacity_ + 1) % (capacity_ + 1)];
}
};
循环队列的实现
下面是完整的循环队列实现:
class CircularQueue {
private:
int* data_;
int head_;
int tail_;
int capacity_;
public:
CircularQueue(int capacity) {
data_ = new int[capacity + 1];
head_ = 0;
tail_ = 0;
capacity_ = capacity;
}
~CircularQueue() {
delete[] data_;
}
int capacity() const { return capacity_; }
int size() const {
return (tail_ - head_ + capacity_ + 1) % (capacity_ + 1);
}
bool empty() const {
return tail_ == head_;
}
bool full() const {
return (tail_ + 1) % (capacity_ + 1) == head_;
}
bool enqueue(int value) {
if (full()) return false;
data_[tail_] = value;
tail_ = (tail_ + 1) % (capacity_ + 1);
return true;
}
bool dequeue() {
if (empty()) return false;
head_ = (head_ + 1) % (capacity_ + 1);
return true;
}
int front() const {
if (empty()) return -1;
return data_[head_];
}
int rear() const {
if (empty()) return -1;
return data_[(tail_ - 1 + capacity_ + 1) % (capacity_ + 1)];
}
};
如何管理循环队列事件?
循环队列可以用于管理事件队列,在这种场景下,入队操作代表事件的到达,出队操作代表事件的处理。事件队列的一个重要应用场景是多线程编程中的任务队列。下面是一个简单的示例程序:
#include
#include
#include
void producer(CircularQueue& q) {
int count = 0;
while (true) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
q.enqueue(count++);
std::cout << "event " << count - 1 << " is produced" << std::endl;
}
}
void consumer(CircularQueue& q) {
while (true) {
if (!q.empty()) {
int data = q.front();
q.dequeue();
std::cout << "event " << data << " is consumed" << std::endl;
}
else {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
}
int main() {
CircularQueue q(10);
std::thread t1(producer, std::ref(q));
std::thread t2(consumer, std::ref(q));
t1.join();
t2.join();
return 0;
}
在这个示例程序中,生产者线程不断产生事件,并将事件加入到事件队列中;消费者线程不断检查事件队列是否为空,如果非空则从队列中取出一个事件并消费它。
总结
循环队列是一种实现队列数据结构的优秀方式,它可以高效地处理连续流数据、实现多线程任务队列等场景。循环队列需要考虑头尾指针的移动、空间的利用方案、队列是否为空或为满等问题。通过合理的队列实现和管理,循环队列可以极大地简化数据处理的复杂度。