顺序队列在使用过程中容易出现虚假的满状态, 为了解决这个问题,就产生了一个较巧妙的方法,将顺序队列臆造为一个环状的空间,称之为循环队列。循环队列中指针和队列元素之间的关系不变,我们只需要利用模运算就可以很容易实现指针的循环移动。但是循环队列中存在一个问题,在循环队列中只凭头指针front等于尾指针rear无法判别队列空间是“空”还是“满”,可有两种处理方法:其一是另设一个标志位以区别队列是“空”还是“满”;其二是少用一个元素空间,约定以“队列头指针在队列尾指针的下一位置(指环状的下一位置)上”作为队列呈“满”状态的标志。此处使用方法二来解决这个问题。
typedef struct CircularQueue{
int* a;//存放数据数组
int front;//头结点
int back;//尾结点后一个,有效数据个数
int k;//元素个数
} CircularQueue;
开辟k+1个空间,k个存放数据,将头尾结点初始化为0,即数组的第一个位置。
代码实现
//队列初始化
CircularQueue* InitQueue(int k)
{
CircularQueue* obj=(CircularQueue*)malloc(sizeof(CircularQueue));//为队列分配空间
obj->a=(int*)malloc(sizeof(int)*(k+1));
obj->front=obj->back=0;//初始队列为空,头尾指针都指向0的位置
obj->k=k;
return obj;
}
测试
根据结构定义,头结点跟尾结点相等时则队列为空。为真则为空,为假则不为空。
代码实现
bool QueueEmpty(CircularQueue* obj) {
return obj->front==obj->back;//头尾结点相等时则为空
}
测试
根据循环队列结构的定义,尾结点后一个是头结点则队列为满。为真则为满,不为真则不满。
代码实现
bool QueueFull(CircularQueue* obj) {
return (obj->back+1)%(obj->k+1)==obj->front;//尾结点的下一个等于头结点则为满
}
测试
根据队列的结构定义,back为尾结点的后一个,当队列空间没有满时,插入数据时则在back位置插入。
代码实现
bool EnQueue(CircularQueue* obj, int value) {
//满了返回false
if(QueueFull(obj))
{
return false;
}
obj->a[obj->back]=value;//插入有效数据
obj->back++;
obj->back%=(obj->k+1);//进入循环,不能超过最大个数
return true;
}
测试
根据队列的定义,只能队头出数据,在循环队列里面通过头尾结点来访问数据,将front++即可删除头结点数据。
代码实现
bool DeQueue(CircularQueue* obj) {
//如果为空返回false
if(QueueEmpty(obj))
{
return false;
}
obj->front++;//出队则头结点往前走
obj->front%=(obj->k+1);
return true;
}
测试
在保证队列不是空的情况下,队头元素为front下标的元素,为空则返回-1
代码实现
int QueueFront(CircularQueue* obj) {
//为空返回-1
if(QueueEmpty(obj))
return -1;
return obj->a[obj->front];
}
测试
在保证队列不是空的情况下,队头元素为back前一个下标的元素,为空则返回-1
代码实现
int QueueRear(CircularQueue* obj) {
//为空返回-1
if(QueueEmpty(obj))
return -1;
//有一种特殊情况 不是back-1
//1.
//return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
return obj->a[(obj->back+obj->k)%(obj->k+1)];
//2.
// if(obj->back==0)
// {
// return obj->a[obj->k];
// }
// else
// {
// return obj->a[obj->back-1];
// }
}
测试
数组是通过队列的指针访问,且数组和队列都是内存是连续的,可以直接释放内存,所以先释放数组,在释放队列
void QueueFree(CircularQueue* obj) {
free(obj->a);//释放数组
free(obj);//释放队列
}
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
typedef int QDataType;
//结构定义
typedef struct CircularQueue{
QDataType* a;//存放数据数组
int front;//头结点
int back;//尾结点后一个,有效数据个数
int k;//元素个数
} CircularQueue;
//初始化
CircularQueue* InitQueue(int k)
{
CircularQueue* obj=(CircularQueue*)malloc(sizeof(CircularQueue));//为队列分配空间
obj->a=(int*)malloc(sizeof(int)*(k+1));
obj->front=obj->back=0;//初始队列为空,头尾指针都指向0的位置
obj->k=k;
return obj;
}
//判断是否为空
bool QueueEmpty(CircularQueue* obj) {
return obj->front==obj->back;//头尾结点相等时则为空
}
//判断是否为满
bool QueueFull(CircularQueue* obj) {
return (obj->back+1)%(obj->k+1)==obj->front;//尾结点的下一个等于头结点则为满
}
//入队
bool EnQueue(CircularQueue* obj, int value) {
//满了返回false
if(QueueFull(obj))
{
return false;
}
obj->a[obj->back]=value;//插入有效数据
obj->back++;
obj->back%=(obj->k+1);//进入循环,不能超过最大个数
return true;
}
//出队
bool DeQueue(CircularQueue* obj) {
//如果为空返回false
if(QueueEmpty(obj))
{
return false;
}
obj->front++;//出队则头结点往前走
obj->front%=(obj->k+1);
return true;
}
//获取队头元素
int QueueFront(CircularQueue* obj) {
//为空返回-1
if(QueueEmpty(obj))
return -1;
return obj->a[obj->front];
}
//获取队尾元素
int QueueRear(CircularQueue* obj) {
//为空返回-1
if(QueueEmpty(obj))
return -1;
//有一种特殊情况 不是back-1
//1.
//return obj->a[(obj->back-1+obj->k+1)%(obj->k+1)];
return obj->a[(obj->back+obj->k)%(obj->k+1)];
//2.
// if(obj->back==0)
// {
// return obj->a[obj->k];
// }
// else
// {
// return obj->a[obj->back-1];
// }
}
//销毁
void QueueFree(CircularQueue* obj) {
free(obj->a);
free(obj);
}
循环队列OJ链接
循环队列
本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!