简介
队列其实和堆栈很像也是一种操作受限制的线性表,插入元素和删除元素操作收到限制。插入只能在队尾插入也就是入列(Enqueue),删除只能在队首删除也就是出列(Dequeue)。这篇文章将用数组作为存储队列元素的方式实现一个循环队列。
定义队列类型
// 宏定义不能有分号,刚才有分号直接报错了说多了个;在]前面
#define SIZE 10
// 如果结构内部不需要指向结构的指针类型,可以使用匿名结构加定义别名的方式
typedef struct {
int data[SIZE];
// 定义头尾下标
int front;
int rear;
} Queue, *QueuePtr;
初始化函数
// 初始化队列,将头尾下标指向-1
void initQueue(QueuePtr queue) {
queue->front = -1;
queue->rear = -1;
}
前后坐标移动
移动rear
我们首先定义rear指向队列尾部,Front指向队列的前一个位置。
因为初始化的时候队列是空的,所以前面两个下标都是-1。
此时如果向队列里面添加元素这两个下标应该如何变化呢?
我们只需要将rear++,此时队列内只有一个元素下标为0此时队列尾部也就是当前加入的元素下标也为0,
rear++后等于0,因为是指向队列尾部所以的操作符合我们之前定义rear指向队列尾部。
移动front
因为front指向队列前面的一个位置,所以往队列里添加数据的时候它并不会移动。
之后再元素出列的时候才移动,如果我们出列一个元素,那么我们可以通过什么方式获取待出列元素的下标呢?
答案也很简单front等于队列前面一个位置,那么front后面一个位置不就是队列位置吗?
所以front++后的结果就是队列当前队首的位置下标。
等队首出列之后front又在队列前面一个位置所以也是符合定义的。
空队列判断
比如我初始化一个队列并让5个元素入列此时rear为4,front为-1。
那么我是不是要出列5次才能把队列清空,也就是front++5次,此时front为4,rear为4。
是不是和初始化rear为-1,front为-1有点类似呢?
也就可以认定rear==front时整个队列是空的?
所以这里我用它们是否相等判断队列是否为空
// 检查当前队列是否空了
bool isQueueEmpty(QueuePtr queue) {
return queue->front == queue->rear;
}
满队列判断
我这里定义rear-front == SIZE - 1;留数组一个空位置给front表示队列前面的一个位置,感觉解释太多也不好直接贴代码了。
// 检查当前队列是否满了
bool isQueueFull(QueuePtr queue) {
// 这里用数组留一个空位的方式标记
return queue->rear - queue->front == SIZE - 1;
}
是否有问题呢?
看到这里你是否会想如果rear++超过了队列数组的下标最大值SIZE-1时咋办呢?
比如我SIZE为5,此时rear为4,front为1,此时队列还没有满 4 - 1 < 5 - 1。
此时我再往队列里入列元素rear++等于5超过了最大下标4。这时候会有问题吗?
其实只要做个处理就没有问题,那我就直接贴代码了。代码注释里有这个问题的答案。
入列操作enqueue
// 入队操作,对rear++
void enqueue(QueuePtr queue, int item) {
// 首先需要对队列判断是否满了
if (isQueueFull(queue)) {
printf("queue is full!!!\n");
} else {
// rear前移动
queue->rear++;
// 对尾部下标和SIZE取模防止下标溢出
(queue->data)[queue->rear % SIZE] = item;
// 测试用
printf("this enqueue num is %d\n", item);
}
}
出列操作dequeue
// 出队操作,对front++
int dequeue(QueuePtr queue) {
int ret = NULL;
// 如果不为空则最前面元素出列
if (!isQueueEmpty(queue)) {
// front前移
queue->front++;
// 如果不知道运算符优先级,可以直接使用左右括号括起来
// 对头部下标和SIZE取模防止下标溢出
ret = (queue->data)[queue->front % SIZE];
} else {
printf("queue is empty!!!\n");
}
return ret;
}
总结
其实上面这个对rear,front和SIZE取模虽然可以保持值在0~SIZE-1之间但是如果这个队列一直在使用不断地出列入列可能最后rear超过了int类型能表达的最大正整数然后变成能表达的最小负整数导致程序出现问题,所以还是每次rear++的时候对其取模防止溢出 0 - SIZE - 1 这个范围。不过这里只是为了能简单算出队列中元素个数所以并没有对此做处理,如果大家想实现一个比较好的队列可以做处理,然后再写个新的判断逻辑判断当前队列是否满了。