一、实现循环队列
题目链接:https://leetcode.cn/problems/design-circular-queue/description/
1.大致思路分析
我们来看看题目描述:

这是 LeetCode 上的一道中等难度的题,一般来说,LeetCode 上简单的题不一定简单,中等一定很麻烦,这道题虽然核心也不难,但是就是麻烦。 在做这道题之前,我们要先来介绍一下什么是循环队列,循环队列是一种特殊的队列,它的首尾在逻辑上是相连的,但是还是队头出数据,队尾入数据,保持队列先进先出的特性,被称为环形缓冲器。 但是它和我们之前实现的队列的最大不同是,之前我们实现的队列如果容量不够是可以扩容的,是可变的,而循环队列的容量确定之后就不能再更改,比如后面我们写初始化方法时,会给定一个值给我们初始化,然后我们开辟好空间后就不再修改它的大小了,所以循环队列的容量一经确定就不会再更改。 那么我们接下来大致来分析一下循环队列的结构,我们是继续使用实现队列时的链表,还是说使用数组呢?其实两个都可以,只是使用链表需要使用循环链表,那么哪一个结构更优呢? 循环队列和普通队列的一个较大区别是,循环队列的大小是固定好了的,当我们去删除数据时,不会直接释放空间,而是会留着存储以后新插入的数据,那么很明显数组的优势更大一些。 因为数组数据的删除可以只改变 size,比如尾删就是让 size--,这样就间接实现了删除,我们访问不到原本的尾数据了,但是其实那个空间还在,只是我们 size--后访问不到了而已。 但是链表数据的删除会释放节点,如果不释放节点的话让它强行留下来就会很麻烦,但是我们循环队列的大小又是固定的,到时候重新申请节点插入到原位置又很麻烦,并且链表的开销比数组要大,所以综上我们实现循环链表最好使用数组。 由于队列要遵循头删尾入,所以我们最好像定义队列一样,定义两个下标 front 和 rear 分别指向循环队列的头和尾,方便进行操作,注意:rear 和之前顺序表的 size 一样,不是指向有效元素中的最后一个元素,而是这个最后一个有效元素的下一个位置。 那么数组怎么实现首尾相连呢?首尾相连就是尾的下一个节点是头,实现这个需要一定的技巧性,要使用模运算,当 rear 或者 front 越界时,模上容量大小就可以让它们重新回到开头,就像首尾相连了一样,如图:

现在我们大致知道了循环队列的底层存储,接下来就边做题边讲解思路。
2.循环队列的结构定义和初始化
结构定义
现在我们知道了使用数组,所以循环队列中肯定要有一个数组,但是我们不知道数组的具体大小,需要到时候根据初始化函数的参数确定,所以我们这里直接用一个指针代替,到时候动态申请空间。 由于队列要遵循头删尾入,所以我们最好像定义队列一样,定义两个下标 front 和 rear 分别指向循环队列的头和尾 (这个尾相当于顺序表中的 size),方便进行操作,还有就是我们要知道申请了多大空间,所以用一个整型变量 capacity 来存储。 那么是否要头删使用数组的效率会很低呢?这个是不需要担心的,因为循环队列的空间不需要释放,所以头删只需要让 front++ 即可,到后面出队列再细讲,那么最后循环队列的大致结构定义如下:
typedef struct {
int* arr;
int capacity;
int rear;
int front;
} MyCircularQueue;
初始化
在初始化函数中给了我们一个参数 k,就是我们要申请的空间大小,我们直接通过动态申请 k 个整型(后面可能会需要更改,我们先实现到这里),然后将容量置为 k,两个指向头和尾的下标置为 0,如下:












