数据结构学习笔记——顺序存储结构实现循环队列

简介: 数据结构学习笔记——顺序存储结构实现循环队列

一、循环队列的定义


前面讲到在通过顺序存储结构来判断顺序队列是否为满队时,提及到会存在“假溢出”现象,这里就可以通过循环队列来解决。


所谓循环队列,也就是将顺序队列中的一维数组首尾相连成环,也就是在逻辑上视为一个环连接起来,其存储类型定义与顺序队列的存储类型定义是一样的,也是定义数组data[MaxSize]和两个指针,即队头指针front指向队头元素,队尾指针rear指向队尾元素,如下:

#define MaxSize 20
//循环队列的定义
typedef struct {
  int data[MaxSize];
  int front,rear; //队头指针和队尾指针
} SqQueue;


例如下图是个空循环队列,其中data[]数组中未存任何数据元素,指针front、rear都指向同一位置,此时队列为空:

1667234478193.jpg


二、循环队列的初始化


循环队列的初始化与顺序队列的初始化代码一样,当Q.front=Q.rear=0时,表示循环队列的初始状态,此时队列为空,如下代码:

//循环队列的初始化
void InitQueue(SqQueue &Q) {
  Q.front=Q.rear=0; //队头指针和队尾指针d都指向0
}


三、判断循环队列是否为空队


循环队列的判空代码与顺序队列也是一样,当判断顺序队列是否为空队时,可以判断队头指针front和队尾指针rear是否相等,即if条件语句为Q.front==Q.rear,如下代码:

//判断循环队列是否为空队
bool EmptyQueue(SqQueue Q){
  if(Q.front==Q.rear) //当队头指针front等于队尾指针rear时队列为空 
  return true;
  else
  return false;
}


四、判断循环队列是否为满队


判断循环队列是否为满队就要另外写代码,由于我们采用循环队列实现队列,我们规定:牺牲一个存储单元来区分是否满队,入队时少使用一个队列单元,即队头指针在队尾指针的下一个位置时,此时作为满队的条件,如下图:

1667234533608.jpg

这里我们可以知道,我们定义的MaxSize=7,即data[MaxSize],采用“%”取余符号,来定义每次入队、出队和判断队列是否为满队的代码逻辑,假定x为数字,MaxSize已知为7,即x%7的所有可能性为0、1、2、3、4、5、6,我们可以通过利用这一点来使队头指针和队尾指针判断和进行某些操作时更方便。

以下给出判断循环队列为满队的判断条件:

if((Q.rear+1)%MaxSize==Q.front)
  return true;


直接通过代码可能无法理解,以下会在入队和出队操作时细讲从而明白,即这里的代码意思就是当队尾指针进1与MaxSize取余的值等于头指针时,此时队列已满,如下:

1667234561046.jpg

我们可知当前队头指针Q.front=1,Q.rear=0,即(Q.rear+1)%MaxSize=(0+1)%7=1%7=1,等于Q.front=1,所以此时队列为满队,此时队头指针在队尾指针的下一个位置。


五、入队(插入操作)


前面已经规定入队时少使用一个队列单元,我们可以得出,每次入队操作时,也就是首先判断队列是否为满队,然后先送值到队列的末尾,然后再将队尾指针Q.rear加1取模(通过%实现)。

1667234580013.jpg

执行一次入队操作,送数据元素的值“A”(从队尾插入,先进先出),队尾指针Q.rear移动加1,如下:

1667234595473.jpg

入队的代码依然是通过取余运算实现,队尾指针加1,即Q.rear=(Q.rear+1)%MaxSize,我们知道不管前面(Q.rear+1)为多少,它与MaxSize(图示中MaxSize=5)取余的结果只可能是0、1、2、3、4,也就是队尾指针Q.rear的每次移动加1。

//入队(插入操作)
bool EnterQueue(SqQueue &Q,int x){
  if((Q.rear+1)%MaxSize==Q.front) //若队列为满,则报错 
  return false;
  Q.data[Q.rear]=x; //送入入队数据元素x的值 
  Q.rear=(Q.rear+1)%MaxSize;  //队尾指针加1取模 
  return true;
}


六、出队(删除操作)


每次出队操作时,也就是首先判断队列是否为空队,然后先取队列的队头元素,然后再将队头指针Q.front加1取模(通过%实现)。

1667234618556.jpg

执行一次出队操作,先取队头元素“A”(A位于队头,先进先出),然后再队头指针Q.front移动加1,如下:

1667234700747.jpg

出队的代码依然是通过取余运算实现,队头指针加1,即Q.front=(Q.front+1)%MaxSize,我们知道不管前面(Q.front+1)为多少,它与MaxSize(图示中MaxSize=5)取余的结果只可能是0、1、2、3、4,也就是队头指针Q.front的每次移动加1。

//出队(删除操作)
bool PopQueue(SqQueue &Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[Q.front];  //取出队头数据元素x的值 
  Q.front=(Q.front+1)%MaxSize;  //队头指针加1取模 
  return true;
}


例如,若用数组A[0……5]来实现循环队列,且当前rear和front的值分别为1和5,当从队列中删除一个元素,再加入两个元素后,求此时rear和front的值。

在循环队列中,每删除一个元素,队头指针front=(front+1)%MaxSize,

即front=(5+1)%6=6%6=0;

每插入一个元素,队尾指针rear=(rear+1)%MaxSize,

加入一个元素后,rear=(1+1)%6=2%6=2,

再加入一个元素后,rear=(2+1)%6=3%6=3,

故最后rear和front的值分别为3和0。


七、读取循环队列队头元素


读取循环队列队头元素首先判断队列是否为空,然后也是通过取余操作,取当前队头指针Q.front所指的数据元素,代码如下:

//读取循环队列队头元素
bool GetHeadQueue(SqQueue Q,int &x) { 
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[(Q.front)%MaxSize];
  return true;
}


八、循环队列的遍历输出


循环队列的遍历输出首先判断队列是否为空,然后与顺序队列的遍历输出代码一样,也是通过一个while循环,循环条件是当队头指针小于队尾指针时一直执行下去,然后每次取队头指针所指的数据元素,之后再加1,从而输出每个数据元素,代码如下:

//循环队列的遍历输出
bool PrintQueue(SqQueue Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  while(Q.front<Q.rear) {
  x=Q.data[Q.front++];
  printf("%d ",x);
  }
  return true;
}


九、循环队列的数据元素个数


首先判断队列是否为空,然后通过队尾指针减队头指针加上MaxSize的值与MaxSize取余,即可得到数据元素个数,代码如下:

//循环队列的数据元素个数
bool NumQueue(SqQueue Q){
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  int num=(Q.rear-Q.front+MaxSize)%MaxSize;
  printf("当前循环队列的数据元素个数为:%d\n",num);
}


例如,已知一个循环队列,其存储空间为数组data[21],front指向队头元素的前一个位置,rear指向队尾元素,假设当前front和rear的值分别为8和3,求当前队列的长度。

(rear-front+MaxSize)%MaxSize=(3-8+21)%21

=16%21

=16

故当前队列长度为16。


十、循环队列的建立


顺序队列的建立程序代码与顺序队列是一样的,通过输入一个值表示要创建的顺序队列的元素个数,然后通过一个for循环建立顺序队列,其中每次通过队列的入队操作,从而向队列中输入数据元素并插入至队列中,代码如下:

//循环队列的建立
void CreateQueue(SqQueue &Q,int x) {
  int number;
  printf("请输入要建立的队列元素个数:\n");
  scanf("%d",&number);
  for(int i=0; i<number; i++) {
  printf("输入第%d个入队的元素:\n",i+1);
  scanf("%d",&x);
  EnterQueue(Q,x);
  }
}


❤️循环队列的完整代码


循环队列的完整代码如下:

#include<stdio.h>
#define MaxSize 20
//循环队列的定义
typedef struct {
  int data[MaxSize];
  int front,rear; //队头指针和队尾指针
} SqQueue;
//循环队列的初始化
void InitQueue(SqQueue &Q) {
  Q.front=Q.rear=0; //队头指针和队尾指针d都指向0
}
//判断循环队列是否为空队
bool EmptyQueue(SqQueue Q){
  if(Q.front==Q.rear) //当队头指针front等于队尾指针rear时队列为空 
  return true;
  else
  return false;
}
//判断循环队列是否为满队
bool FullQueue(SqQueue Q){
  if((Q.rear+1)%MaxSize==Q.front)
  return true;
  else
  return false;
}
//入队(插入操作)
bool EnterQueue(SqQueue &Q,int x){
  if((Q.rear+1)%MaxSize==Q.front) //若队列为满,则报错 
  return false;
  Q.data[Q.rear]=x; //送入入队数据元素x的值 
  Q.rear=(Q.rear+1)%MaxSize;  //队尾指针加1取模 
  return true;
}
//出队(删除操作)
bool PopQueue(SqQueue &Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[Q.front];  //取出队头数据元素x的值 
  Q.front=(Q.front+1)%MaxSize;  //队头指针加1取模 
  return true;
}
//读取循环队列队头元素
bool GetHeadQueue(SqQueue Q,int &x) { 
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[(Q.front)%MaxSize];
  return true;
}
//循环队列的遍历输出
bool PrintQueue(SqQueue Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  while(Q.front<Q.rear) {
  x=Q.data[Q.front++];
  printf("%d ",x);
  }
  return true;
}
//循环队列的数据元素个数
bool NumQueue(SqQueue Q){
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  int num=(Q.rear-Q.front+MaxSize)%MaxSize;
  printf("当前循环队列的数据元素个数为:%d\n",num);
}
//循环队列的建立
void CreateQueue(SqQueue &Q,int x) {
  int number;
  printf("请输入要建立的队列元素个数:\n");
  scanf("%d",&number);
  for(int i=0; i<number; i++) {
  printf("输入第%d个入队的元素:\n",i+1);
  scanf("%d",&x);
  EnterQueue(Q,x);
  }
}


一个简单的循环队列的基本实现例子


例如,创建一个循环队列,其初始数据为{1,5,7,3},首先遍历输出该队列,然后读取队头元素,此时输出当前循环队列的数据元素个数,执行一次入队操作,入队元素为{6},此时读取当前的队头元素,执行一次出队操作,此时再读取当前的队头元素,最后遍历输出当前队列。


代码如下:

#include<stdio.h>
#define MaxSize 20
//循环队列的定义
typedef struct {
  int data[MaxSize];
  int front,rear; //队头指针和队尾指针
} SqQueue;
//循环队列的初始化
void InitQueue(SqQueue &Q) {
  Q.front=Q.rear=0; //队头指针和队尾指针d都指向0
}
//判断循环队列是否为空队
bool EmptyQueue(SqQueue Q){
  if(Q.front==Q.rear) //当队头指针front等于队尾指针rear时队列为空 
  return true;
  else
  return false;
}
//判断循环队列是否为满队
bool FullQueue(SqQueue Q){
  if((Q.rear+1)%MaxSize==Q.front)
  return true;
  else
  return false;
}
//入队(插入操作)
bool EnterQueue(SqQueue &Q,int x){
  if((Q.rear+1)%MaxSize==Q.front) //若队列为满,则报错 
  return false;
  Q.data[Q.rear]=x; //送入入队数据元素x的值 
  Q.rear=(Q.rear+1)%MaxSize;  //队尾指针加1取模 
  return true;
}
//出队(删除操作)
bool PopQueue(SqQueue &Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[Q.front];  //取出队头数据元素x的值 
  Q.front=(Q.front+1)%MaxSize;  //队头指针加1取模 
  return true;
}
//读取循环队列队头元素
bool GetHeadQueue(SqQueue Q,int &x) { 
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  x=Q.data[(Q.front)%MaxSize];
  return true;
}
//循环队列的遍历输出
bool PrintQueue(SqQueue Q,int x) {
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  while(Q.front<Q.rear) {
  x=Q.data[Q.front++];
  printf("%d ",x);
  }
  return true;
}
//循环队列的数据元素个数
bool NumQueue(SqQueue Q){
  if(Q.front==Q.rear) //若队列为空,则报错 
  return false;
  int num=(Q.rear-Q.front+MaxSize)%MaxSize;
  printf("当前循环队列的数据元素个数为:%d\n",num);
}
//循环队列的建立
void CreateQueue(SqQueue &Q,int x) {
  int number;
  printf("请输入要建立的队列元素个数:\n");
  scanf("%d",&number);
  for(int i=0; i<number; i++) {
  printf("输入第%d个入队的元素:\n",i+1);
  scanf("%d",&x);
  EnterQueue(Q,x);
  }
}
//主函数
int main() {
  SqQueue Q;
  int x,e;
  InitQueue(Q); //初始化
  CreateQueue(Q,x); //创建队列
  printf("遍历输出当前队列元素:\n");
  PrintQueue(Q,x);  //遍历输出队列
  printf("\n");
  GetHeadQueue(Q,x);  //读取队列的队头元素
  printf("读取队列的队头元素,当前队头元素为:%d\n",x);
  NumQueue(Q);
  printf("请输入一个要入队的元素:");
  scanf("%d",&e);
  EnterQueue(Q,e);  //入队操作 
  GetHeadQueue(Q,x);
  printf("读取队列的队头元素,当前队头元素为:%d\n",x);
  NumQueue(Q);
  PopQueue(Q,x);  //出队操作 
  GetHeadQueue(Q,x);
  printf("执行一次出队操作后,当前队头元素为:%d\n",x);
  printf("遍历输出当前队列元素:\n");
  PrintQueue(Q,x);  //遍历输出队列
}

运行结果如下:

1667234173355.jpg


1667234173355.jpg

相关文章
|
20天前
|
存储 算法
【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)
【数据结构和算法】--- 二叉树(4)--二叉树链式结构的实现(2)
14 0
|
4天前
【数据结构OJ题】链表的回文结构
牛客题目——链表的回文结构
6 0
【数据结构OJ题】链表的回文结构
|
19天前
|
存储 Python
Python中使用列表和字典来存储和处理复杂的数据结构
Python中使用列表和字典来存储和处理复杂的数据结构
|
20天前
|
存储 算法 调度
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
【数据结构与算法】详解循环队列:基于数组实现高效存储与访问
|
2天前
|
存储 数据可视化 数据处理
`geopandas`是一个开源项目,它为Python提供了地理空间数据处理的能力。它基于`pandas`库,并扩展了其对地理空间数据(如点、线、多边形等)的支持。`GeoDataFrame`是`geopandas`中的核心数据结构,它类似于`pandas`的`DataFrame`,但包含了一个额外的地理列(通常是`geometry`列),用于存储地理空间数据。
`geopandas`是一个开源项目,它为Python提供了地理空间数据处理的能力。它基于`pandas`库,并扩展了其对地理空间数据(如点、线、多边形等)的支持。`GeoDataFrame`是`geopandas`中的核心数据结构,它类似于`pandas`的`DataFrame`,但包含了一个额外的地理列(通常是`geometry`列),用于存储地理空间数据。
4 0
|
19天前
|
存储 自然语言处理 NoSQL
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之倒排索引(三)
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之倒排索引(三)
|
19天前
|
存储 自然语言处理 NoSQL
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之列存(二)
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之列存(二)
|
19天前
|
存储 JSON NoSQL
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)
深入解析Elasticsearch的内部数据结构和机制:行存储、列存储与倒排索引之行存(一)
|
19天前
|
存储 缓存 NoSQL
Redis为什么速度快:数据结构、存储及IO网络原理总结
Redis为什么速度快:数据结构、存储及IO网络原理总结
|
19天前
|
存储 算法 Java
Java数据结构与算法:用于高效地存储和检索字符串数据集
Java数据结构与算法:用于高效地存储和检索字符串数据集