例题:循环队列OJ
循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。
循环队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。
循环队列的特点
1.循环队列的大小是固定的
2.符合队列的先进先出
循环队列可以用数组实现,也可以用链表实现。两种实现都差不太多,在结构方面有些不同。
队列也可以用数组实现,只是会产生假溢出问题,实现出来的性能不高,数组实现的循环队列解决了假溢出的问题,性能略高于链表实现的循环队列
用链表实现的循环队列,其实就是一个循环的单链表,只是增加了队列的先进先出的特性,已及大小固定。
typedef struct {
int* arr;
//定义头指针和尾指针
//头指针就是当前元素的下标
//尾指针是尾元素的下一个位置的下标
int head;
int tail;
int k;
} MyCircularQueue;
//判空和判满函数在最后面,声明一下,方便使用
bool myCircularQueueIsFull(MyCircularQueue* obj);
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
//初始化,循环队列的空间是固定大小的
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->arr=(int*)malloc(sizeof(int)*(k+1));
obj->head=obj->tail=0;
obj->k=k;
return obj;
}
//向循环队列中增加元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
//增加元素后,尾指针tail要一道尾元素的下一个位置
//要是尾元素在最后,tail就要循环到首元素的位置,所以得取模
obj->arr[obj->tail]=value;
obj->tail=(obj->tail+1)%(obj->k+1);
return true;
}
//删除循环队列中的元素
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
//移动头指针就是删除元素了
//也要考虑极端情况
obj->head=(obj->head+1)%(obj->k+1);
return true;
}
//获取头部元素
int myCircularQueueFront(MyCircularQueue* obj) {
//判空
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->arr[obj->head];
}
//获取尾部元素
int myCircularQueueRear(MyCircularQueue* obj) {
//判空
if(myCircularQueueIsEmpty(obj))
return -1;
//极端情况,当tail在首元素的位置时,取尾元素就要到下标为元素个数的位置取
//将tail加上元素个数,再模队列的长度就是尾元素的下标了
int i=(obj->tail+obj->k)%(obj->k+1);
return obj->arr[i];
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->head==obj->tail;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
assert(obj);
return obj->head==(obj->tail+1)%(obj->k+1);
}
//销毁
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->arr);
free(obj);
}
typedef struct Node
{ //定义存储数据的结点结构体
int data;
struct Node* next;
}Node;
typedef struct {
//头指针和尾指针
Node*head;
Node*tail;
//队列长
int k;
//计数器
int sz;
} MyCircularQueue;
//判空,判满的声明
bool myCircularQueueIsEmpty(MyCircularQueue* obj);
bool myCircularQueueIsFull(MyCircularQueue* obj);
//初始化
MyCircularQueue* myCircularQueueCreate(int k) {
MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
obj->k=k;
obj->sz=0;
obj->head=obj->tail=NULL;
return obj;
}
//向循环队列中增加元素
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull(obj))
return false;
Node*newnode=(Node*)malloc(sizeof(Node));
assert(newnode);
newnode->data=value;
newnode->next=NULL;
if(myCircularQueueIsEmpty(obj))
{
obj->head=obj->tail=newnode;
obj->sz++;
}
else
{
obj->tail->next=newnode;
obj->tail=newnode;
obj->sz++;
}
return true;
}
//删除循环队列中的元素,删除后需销毁动态内存
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return false;
Node*next=obj->head->next;
free(obj->head);
obj->head=next;
obj->sz--;
return true;
}
//返回循环队列的队头
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->head->data;
}
//返回循环队列的队尾
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty(obj))
return -1;
return obj->tail->data;
}
//判空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
assert(obj);
return obj->sz==0;
}
//判满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
assert(obj);
return obj->sz>=obj->k;
}
//销毁动态内存,要用循环将结点一个一个销毁
void myCircularQueueFree(MyCircularQueue* obj) {
Node*cur=obj->head;
while(cur)
{
Node*next=cur->next;
free(cur);
cur=next;
}
free(obj);
}
初次看循环队列的时候有点懵,没咋理解,后来又返回去看了一遍,这下全看懂了。相对来说,数组实现的循环队列比链表实现的循环队列要难点,就是那个循环点那儿得小心分析,不然就掉坑里了。链表实现循环队列感觉就是写循环单链表的感觉。