队列的概念
排队是生活中非常常见的现象,比如我们排队进游乐场,排在你前面的肯定是比你早排队的人,排在你后面的肯定是比你晚排队的人,换成专业点的语言也就是FIFO(First In, First Out,先进先出) 这就是队列了。和栈支持入栈push()和出栈pop()类似,队列支持入队enqueue()和出队dequeue()的操作。enqueue()是从队尾塞一个元素进去,dequeue()是从队头拿一个元素出来。
队列的实现
基于链表实现的队列还是和栈很类似,栈是从头进从头出,队列是从尾进从头出。而基于数组实现的队列有点点复杂,我们需要两个“指针”,tail和head,因为是数组所以我们可以用下标的加减来起到指针的作用。现在最重要的问题来了,如何判断队列是否满了?经过多次画图观察,我们发现tail - head - 1 = capacity时,队列满了。 而当tail移动到最后一个位置length-1而head不在第一个位置-1时,我们可以认为这个数组内有空间,所以启动整体平移将数组内元素都往前移动一个单位,这样在tail的位置就有新的空间来塞入新元素了。整体平移要花个O(n)的时间。具体请看最后一部分的代码
是不是觉得上面的方法有点傻,挪head和tail的位置明显更简单嘛,所以下面就是循环链表(并没有真的把数组头尾连接,而是把数组掰弯来看),只要我们找出判断队满的条件就可以了,其实不难发现,结束条件和上面的是一样的,只不过我们要在enqueue()和front()时采取%capacity(%代表mod,%是C++求余数的符号)的方法来使得数组不会越界。具体请看最后一部分的代码
总结
说着容易做起来难。总结一下几个难点!
- 画图的时候一定要明确初始位置的head = -1,tail = 0,这个一定要在图上体现出来
- 写代码的时候同样要注意初始位置的head = -1,tail = 0,不然很难判断明白IsFull()的判定条件。
队列的数组版实现
main.cpp
#include <iostream>
#include "MyStack.h"
#include "MyQueue.h"
#include "CircularQueue.h"
using namespace std;
int main() {
/********* Normal Queue ********/
MyQueue q(5);
cout << boolalpha << "Can we dequeue? " << q.dequeue() << endl; // 测试空的情况下能不能dequeue
cout << boolalpha << "Is queue empty? " << q.IsEmpty() << endl;// 测试空的情况下的返回值
cout << q.front() << endl; // 测试空的情况下能不能返回队头
q.enqueue(111); // 测试enqueue
cout << boolalpha << "Is queue empty? " << q.IsEmpty() << endl;// 测试非空的情况下的返回值
cout << q.front() << endl; // 测试非空的情况下能不能返回栈顶
cout << boolalpha << "Can we dequeue? " << q.dequeue() << endl; // 测试非空的情况下能不能dequeue
q.enqueue(2);
q.enqueue(3);
q.enqueue(4);
q.enqueue(5);
q.enqueue(6);
cout << boolalpha << "Is queue full? " << q.IsFull() << endl; // 测试是否满了,满了之后会返回真
q.showQueue();
q.dequeue();
cout << boolalpha << "Is queue full? " << q.IsFull() << endl; // 测试是否整体平移
cout << boolalpha << "Can we enqueue? " << q.enqueue(7) << endl; //测试整体平移后还能不能enqueue
q.showQueue();
cout << endl;
/********* Circular Queue ********/
CircularQueue cq(5);
cout << boolalpha << "Can we dequeue? " << cq.dequeue() << endl; // 测试空的情况下能不能dequeue
cout << boolalpha << "Is queue empty? " << cq.IsEmpty() << endl;// 测试空的情况下的返回值
cout << cq.front() << endl; // 测试空的情况下能不能返回队头
cq.enqueue(111); // 测试enqueue
cout << boolalpha << "Is queue empty? " << cq.IsEmpty() << endl;// 测试非空的情况下的返回值
cout << cq.front() << endl; // 测试非空的情况下能不能返回栈顶
cout << boolalpha << "Can we dequeue? " << cq.dequeue() << endl; // 测试非空的情况下能不能dequeue
cq.enqueue(2);
cq.enqueue(3);
cq.enqueue(4);
cq.enqueue(5);
cq.enqueue(6);
cout << boolalpha << "Is queue full? " << cq.IsFull() << endl; // 测试是否满了,满了之后会返回真
cq.showQueue();
cq.dequeue();
cout << boolalpha << "Is queue full? " << cq.IsFull() << endl; // 测试是否整体平移
cout << boolalpha << "Can we enqueue? " << cq.enqueue(7) << endl; //测试整体平移后还能不能enqueue
cq.showQueue();
return 0;
}
普通队列
MyQueue.h
//
// Created by Sky on 2021/3/14.
//
#ifndef DATA_STRUCTURE_PRACTICES_MYSTACK_H
#define DATA_STRUCTURE_PRACTICES_MYSTACK_H
#include <iostream>
using namespace std;
class MyStack{
public:
MyStack(int n);
bool isEmpty();
bool isFull();
bool push(int num);
bool pop();
int top();
~MyStack();
private:
void expand();
int* data;
int capacity;
int size;
};
#endif //DATA_STRUCTURE_PRACTICES_MYSTACK_H
MyQueue.cpp
//
// Created by Sky on 2021/3/14.
//
#include "MyStack.h"
MyStack::MyStack(int n) {
int* temp = new int[n];
data = temp;
capacity = n;
size = 0;
cout << "Stack has been created" << endl;
}
MyStack::~MyStack() {
delete data;
cout << "Stack has been deleted" << endl;
}
bool MyStack::isFull() {
if(capacity == size){
expand();
return true;
}
return false;
}
bool MyStack::isEmpty() {
if(size == 0)
return true;
return false;
}
bool MyStack::push(int num) {
if (isFull())
return false;
data[size++] = num;
return true;
}
bool MyStack::pop() {
if(isEmpty())
return false;
--size;
return true;
}
int MyStack::top() {
if (isEmpty())
return -1;
// cout << data[size] << endl;
return data[size-1];
}
void MyStack::expand() {
int* temp = new int [2*capacity];
for (int i = 0; i < capacity; i++){
temp[i] = data[i];
}
delete data;
data = temp;
capacity *= 2;
}
循环队列
CircularQueue.h
//
// Created by Sky on 2021/3/15.
//
#ifndef DATA_STRUCTURE_PRACTICES_CIRCULARQUEUE_H
#define DATA_STRUCTURE_PRACTICES_CIRCULARQUEUE_H
# include <iostream>
using namespace std;
class CircularQueue {
public:
CircularQueue(int n);
~CircularQueue();
bool IsEmpty();
bool IsFull();
bool enqueue(int n);
bool dequeue();
int front();
void showQueue();
private:
int* data;
int capacity;
int head;
int tail;
};
#endif //DATA_STRUCTURE_PRACTICES_CIRCULARQUEUE_H
CircularQueue.cpp
//
// Created by Sky on 2021/3/15.
//
#include "CircularQueue.h"
CircularQueue::CircularQueue(int n) {
int* temp = new int [n];
data = temp;
capacity = n;
head = -1;
tail = 0;
cout << "Queue has been created" << endl;
}
CircularQueue::~CircularQueue() {
delete data;
cout << "Queue has been deleted" << endl;
}
bool CircularQueue::IsEmpty() {
if (tail - head == 1)
return true;
return false;
}
bool CircularQueue::IsFull() {
if(tail - head - 1 == capacity)
return true;
return false;
}
bool CircularQueue::enqueue(int n) {
if(IsFull())
return false;
data[tail%capacity] = n;
tail++;
return true;
}
bool CircularQueue::dequeue() {
if(IsEmpty())
return false;
head++;
return true;
}
int CircularQueue::front() {
if(IsEmpty())
return -1;
return data[head%capacity+1];
}
void CircularQueue::showQueue() {
cout << "head = " << head << ", tail = " << tail << endl;
int count = 1;
for(int i = head+1; i%capacity < tail-head-1; i++){
cout << data[i%capacity] << ' ';
if (count >= tail-head-1)
break;
count++;
}
cout << endl;
}