目录
一、循环队列介绍
1、题目描述
2、思路
循环队列遵循队列的特征尾进头出(FIFO),所以在设计的时候我们需要保证像队列(Queue)的实现一样,包含头尾指针。
那么问题来了,我们应该选择什么样的存储结构解决这个问题呢?(顺序/链式)
ok,我们两个都要。
1.顺序结构
数组是线性的,遵循尾进头出的原则
1.我们需要在头指针元素销毁后,向后移动头指针。
2.我们需要在插入新元素时,向后移动尾指针。
3.当尾指针到达数组底部后,将其调至头指针之前的空位。
4.特别注意,为了判断队列的满/空,我们需要在队列可存储大小k之上再加一个位置,用来判断队列的满/空,front==rear(empty),rear+1=front(full)
2.链式结构
其实更好理解,相当于不断创建一个长度小于等于k的单链表。单链表会像火车一样不断往后开。
每当创建新节点,创建tail->next节点,tail往后移动。
每当删除节点,删除head指向节点,head也往后移动。
可以完全还原循环链表的功能。
但我觉得这样做是不是有点无聊。。。
二、顺序结构代码实现
1.设计结构体
数组 头尾指针 数组真实记录数据长度为k,而我们需要创造的长度为k+1,留一个空位用来进行前文提及的判断空/满。
要注意的是,front&back的数值可能会超过数组容量大小,这是因为front&back只是两个整数,用来记录头尾坐标的,在真正实现中会将其取模以达到循环的效果,大家看起来好像是数组扩张了。
typedef struct {
int* a;
int front;
int back;
int k;//用来记录队列长度
} MyCircularQueue;
2.判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->front == obj->back;
}
3.判断队列是否为满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return (obj->back + 1) % (obj->k + 1) == obj->front;
}
4.创建循环队列
注意这里的malloc创建了k+1个空间
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->a = (int*)malloc(sizeof(int) * (k + 1));
obj->front = 0;
obj->back = 0;
obj->k = k;
return obj;
}
5.入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (myCircularQueueIsFull(obj)) {
return false;
}
obj->a[obj->back] = value;
obj->back++;
obj->back %= (obj->k + 1);
return true;
}
6.出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return false;
}
++obj->front;
obj->front %= (obj->k + 1);
return true;
}
7.返回队头数据
int myCircularQueueFront(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return -1;
}
return obj->a[obj->front];
}
8.返回队尾数据
int myCircularQueueRear(MyCircularQueue* obj) {
if (myCircularQueueIsEmpty(obj)) {
return -1;
}
if (obj->back == 0) {
return obj->a[obj->k];
}
else {
return obj->a[obj->back - 1];
}
}
9.销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
三、链式结构代码实现
1.设计结构体
创建头尾节点,容量,节点个数。
typedef struct {
struct ListNode* head;
struct ListNode* tail;
int capacity;
int size;
} MyCircularQueue;
2.判断队列是否为空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->size == 0;
}
3.判断队列是否为满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return obj->size == obj->capacity;
}
4.创建循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->capacity = k;
obj->size = 0;
obj->head = obj->tail = NULL;
return obj;
}
5.入队
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if (obj->size >= obj->capacity) {
return false;
}
struct ListNode* node = (struct ListNode*)malloc(sizeof(struct ListNode));
node->val = value;
node->next = NULL;
if (!obj->head) {
obj->head = obj->tail = node;
}
else {
obj->tail->next = node;
obj->tail = node;
}
obj->size++;
return true;
}
6.出队
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if (obj->size == 0) {
return false;
}
struct ListNode* node = obj->head;
obj->head = obj->head->next;
obj->size--;
free(node);
return true;
}
7.返回队头数据
int myCircularQueueFront(MyCircularQueue* obj) {
if (obj->size == 0) {
return -1;
}
return obj->head->val;
}
8.返回队尾数据
int myCircularQueueRear(MyCircularQueue* obj) {
if (obj->size == 0) {
return -1;
}
return obj->tail->val;
}
9.销毁队列
void myCircularQueueFree(MyCircularQueue* obj) {
for (struct ListNode* curr = obj->head; curr;) {
struct ListNode* node = curr;
curr = curr->next;
free(node);
}
free(obj);
}
四、总结
其实两种结构的实现,均不是循环结构,都是线性结构,不由得感叹人类的智慧是无穷的,这也告诉我们做题不要那么死板,有时候奇怪的思路胜过暴力的解法。一起加油,各位。