目录
题目信息:
设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
你的实现应该支持如下操作:
MyCircularQueue(k)
: 构造器,设置队列长度为 k 。Front
: 从队首获取元素。如果队列为空,返回 -1 。Rear
: 获取队尾元素。如果队列为空,返回 -1 。enQueue(value)
: 向循环队列插入一个元素。如果成功插入则返回真。deQueue()
: 从循环队列中删除一个元素。如果成功删除则返回真。isEmpty()
: 检查循环队列是否为空。isFull()
: 检查循环队列是否已满。
思路分析:
思路一:链表
结构体的设计:
链表实现循环队列为了便于取头部元素和尾部元素最好创建两个结构体,一个结构体放置链表信息,另一个结构体封装循环队列元素,其中封装循环队列元素的结构体放置head、tail、preTail、size、capacity。head指向头元素,用于获取头元素信息;tail指向尾元素的后一个元素,用来在队尾插入元素,preTail指向尾元,用于获取尾元素;size表示循环队列内元素个数;capacity表示循环队列内开辟的元素个数
typedef struct Qnode
{
struct Qnode* next;
int val;
}Qnode;
typedef struct MyCircularQueue {
Qnode* head;
Qnode* tail;
Qnode* preTail;
int size;
int capacity;
} MyCircularQueue;
myCircleQueueCreate:
先对封装循环队列元素的结构体进行malloc开辟,将capacity置为参数k,在对循环链表每一个节点进行malloc开辟,配合while循环语句,开辟一个长度为k的循环队列
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
q->size = 0;
q->capacity = k;
//创建第一个空间
q->head = (Qnode*)malloc(sizeof(Qnode));
q->tail = q->head;
q->preTail = NULL;
Qnode* cur = q->head;
//创建k个节点
while (k--)
{
cur->next = (Qnode*)malloc(sizeof(Qnode));
cur = cur->next;
}
cur->next = q->head;
return q;
}
myCircleQueueFront:
先判断队列的是否有元素,没有元素直接返回-1,有元素返回head节点的元素
myCircleQueueRear:
先判断队列的是否有元素,没有元素直接返回-1,有元素返回preTail节点的元素
//循环队列取对头元素
int myCircularQueueFront(MyCircularQueue* obj) {
return obj->size == 0 ? -1 : obj->head->val;
}
//循环队列取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
return obj->size == 0 ? -1 : obj->preTail->val;
}
myCircleQueueEnQueue:
先判断队列是否满了,满了返回false,没满执行尾插:
将tail节点内存储元素,preTail指向tail节点的下一个,作为队尾,tail再指向preTail节点的下一个,用于下一个元素的插入,元素个数+1
//循环队列的插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//队列满了插入失败
if (obj->tail->next == obj->head)
return false;
//插入数据
obj->tail->val = value;
obj->preTail = obj->tail;
obj->tail = obj->tail->next;
obj->size++;
return true;
}
myCircleQueueDnQueue:
先判断队列是否为空,为空返回false,不为空执行头删:
tail节点指向tail节点的下一个,元素个数-1
//循环队列的删除
bool myCircularQueueDnQueue(MyCircularQueue* obj) {
//队列为空删除失败
if (obj->size == 0)
return false;
//链表头删(不要销free节点的空间)
obj->head = obj->head->next;
obj->size--;
return true;
}
myCircleQueueIsEmpty:
判断循环队列的元素个数是不是0
//循环队列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->size == 0;
}
myCircleQueueIsFull:
判断循环队列的元素个数是不是等于队列长度
//循环队列判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return obj->size == obj->capacity;
}
思路二:数组
结构体的设计:
数组实现循环队列,创建的结构体元素包括指针a,头元素下标front,尾元素的下一个元素的下标rear和循环队列元素个数k
typedef struct MyCircleQueue
{
int* a;
int front;
int rear;
int k;
}MQ;
MyCricleQueueCreate:
开辟一个长度为k+1的数组(用于避免假溢出问题),结构体元素指针a指向开辟的数组的首元素的地址,front指向首元素下标,rear指向尾元素的下一个元素的下标,用来尾插元素,元素个数置为k
//循环队列的创建
MQ* myCircleQueueCreate(int k)
{
MQ* obj = (MQ*)malloc(sizeof(MQ));
int* arr = (int *)malloc(sizeof(int) * (k + 1));
obj->a = arr;
obj->front = 0;
obj->rear = 0;
obj->k = k;
return obj;
}
myCircleQueueFront:
先判断循环队列是否满了,满了就返回-1,没满就返回front下标的元素
//循环队列取队头元素
int myCircleQueueFront(MQ* obj)
{
if (myCircleQueueIsEmpty)
{
return -1;
}
return obj->a[obj->front];
}
myCircleQueueRear:
先判断rear下标是否为0,如果下标为0,说明循环队列已经执行循环了,此时队尾元素就是下标为k-1的元素,如果rear下标不为0,则说明循环队列没有执行循环,此时队尾元素就是下标为rear-1的元素
//循环队列取队尾元素
int myCircleQueueRear(MQ* obj)
{
if (obj->rear == 0)
{
return obj->a[obj->k - 1];
}
else
{
return obj->a[obj->rear - 1];
}
}
myCircleQueueEnQueue:
先判断循环队列是否满了,如果满了,返回false,没满执行尾插:
将元素插入到数组rear下标所在位置,rear+1,判断+1后的rear是否超出循环队列可存储元素的个数,用rear/k+1,得到一个在(0,k)范围内的下标
//循环队列添加元素
bool myCircleQueueEnQueue(MQ* obj, int val)
{
if (myCircleQueueIsFull)
{
return false;
}
obj->a[obj->rear] = val;
obj->rear++;
obj->rear = (obj->rear) / (obj->k + 1);
return true;
}
myCircleQueueDnQueue:
先判断循环队列是否为空,如果为空,返回false,没空执行头删:
执行front+1,判断+1后的front是否超出循环队列可存储元素的个数,用front/k+1,得到一个在(0,k)范围内的下标
//循环队列删除元素
bool myCircleQueueDnQueue(MQ* obj)
{
if (myCircleQueueIsEmpty)
{
return false;
}
obj->front++;
obj->front = (obj->front) / (obj->k + 1);
return true;
}
myCircleQueueIsEmpty:
判断头元素头元素下标和尾元素的后一个元素的下标是否相同
//循环队列的判断空
bool myCircleQueueIsEmpty(MQ* obj)
{
return obj->front == obj->rear;
}
myCircleQueueIsFull:
//循环队列的判断满
bool myCircleQueueIsFull(MQ* obj)
{
return (obj->rear + 1) / (obj->k + 1) == obj->front;
}
源码:
链表:
Circle.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef struct Qnode
{
struct Qnode* next;
int val;
}Qnode;
typedef struct MyCircularQueue {
Qnode* head;
Qnode* tail;
Qnode* preTail;
int size;
int capacity;
} MyCircularQueue;
//循环队列的创建
MyCircularQueue* myCircularQueueCreate(int k);
//循环队列的插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value);
//循环队列的删除
bool myCircularQueueDnQueue(MyCircularQueue* obj);
//循环队列取队头元素
int myCircularQueueFront(MyCircularQueue* obj);
//循环队列取队尾元素
int myCircularQueueRear(MyCircularQueue* obj);
//循环队列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
//循环队列判满
bool myCircularQueueIsFull(MyCircularQueue* obj);
//循环队列的释放
void myCircularQueueFree(MyCircularQueue* obj);
Cricle.c
#include "circle.h"
//循环队列的创建
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
q->size = 0;
q->capacity = k;
//创建第一个空间
q->head = (Qnode*)malloc(sizeof(Qnode));
q->tail = q->head;
q->preTail = NULL;
Qnode* cur = q->head;
//创建k个节点
while (k--)
{
cur->next = (Qnode*)malloc(sizeof(Qnode));
cur = cur->next;
}
cur->next = q->head;
return q;
}
//循环队列的插入
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
//队列满了插入失败
if (obj->tail->next == obj->head)
return false;
//插入数据
obj->tail->val = value;
obj->preTail = obj->tail;
obj->tail = obj->tail->next;
obj->size++;
return true;
}
//循环队列的删除
bool myCircularQueueDnQueue(MyCircularQueue* obj) {
//队列为空删除失败
if (obj->size == 0)
return false;
//链表头删(不要销free节点的空间)
obj->head = obj->head->next;
obj->size--;
return true;
}
//循环队列取对头元素
int myCircularQueueFront(MyCircularQueue* obj) {
return obj->size == 0 ? -1 : obj->head->val;
}
//循环队列取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
return obj->size == 0 ? -1 : obj->preTail->val;
}
//循环队列判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
return obj->size == 0;
}
//循环队列判慢
bool myCircularQueueIsFull(MyCircularQueue* obj) {
return obj->size == obj->capacity;
}
//循环队列的释放
void myCircularQueueFree(MyCircularQueue* obj) {
//需要释放k+1个节点
int cnt = obj->capacity + 1;
Qnode* cur = obj->head;
while (cnt--)
{
Qnode* tmp = cur->next;
free(cur);
cur = tmp;
}
free(obj);
}
数组:
Queue.h
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>
typedef struct MyCircleQueue
{
int* a;
int front;
int rear;
int k;
}MQ;
//循环队列的创建
MQ* myCircleQueueCreate(int k);
//循环队列的判断空
bool myCircleQueueIsEmpty(MQ* obj);
//循环队列的判断满
bool myCircleQueueIsFull(MQ* obj);
//循环队列添加元素
bool myCircleQueueEnQueue(MQ* obj, int val);
//循环队列删除元素
bool myCircleQueueDnQueue(MQ* obj);
//循环队列取队头元素
int myCircleQueueFront(MQ* obj);
//循环队列取队尾元素
int myCircleQueueRear(MQ* obj);
//循环队列的释放
void myCircleQueueFree(MQ* obj);
Queue.c
#include "Queue.h"
//循环队列的创建
MQ* myCircleQueueCreate(int k)
{
MQ* obj = (MQ*)malloc(sizeof(MQ));
int* arr = (int *)malloc(sizeof(int) * (k + 1));
obj->a = arr;
obj->front = 0;
obj->rear = 0;
obj->k = k;
}
//循环队列的判断空
bool myCircleQueueIsEmpty(MQ* obj)
{
return obj->front == obj->rear;
}
//循环队列的判断满
bool myCircleQueueIsFull(MQ* obj)
{
return (obj->rear + 1) / (obj->k + 1) == obj->front;
}
//循环队列添加元素
bool myCircleQueueEnQueue(MQ* obj, int val)
{
if (myCircleQueueIsFull)
{
return false;
}
obj->a[obj->rear] = val;
obj->rear++;
obj->rear = (obj->rear) / (obj->k + 1);
return true;
}
//循环队列删除元素
bool myCircleQueueDnQueue(MQ* obj)
{
if (myCircleQueueIsEmpty)
{
return false;
}
obj->front++;
obj->front = (obj->front) / (obj->k + 1);
return true;
}
//循环队列取队头元素
int myCircleQueueFront(MQ* obj)
{
if (myCircleQueueIsEmpty)
{
return -1;
}
return obj->a[obj->front];
}
//循环队列取队尾元素
int myCircleQueueRear(MQ* obj)
{
//方式1:判断
if (obj->rear == 0)
{
return obj->a[obj->k - 1];
}
//方式2:取%
return (obj->a[obj->rear - 1 + obj->k + 1]) / obj->a[obj->k + 1];
}
//循环队列的释放
void myCircleQueueFree(MQ* obj)
{
free(obj->a);
free(obj);
}