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

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

一、循环队列的定义


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


所谓循环队列,也就是将顺序队列中的一维数组首尾相连成环,也就是在逻辑上视为一个环连接起来,其存储类型定义与顺序队列的存储类型定义是一样的,也是定义数组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

相关文章
|
2月前
|
存储 搜索推荐 算法
【数据结构】树型结构详解 + 堆的实现(c语言)(附源码)
本文介绍了树和二叉树的基本概念及结构,重点讲解了堆这一重要的数据结构。堆是一种特殊的完全二叉树,常用于实现优先队列和高效的排序算法(如堆排序)。文章详细描述了堆的性质、存储方式及其实现方法,包括插入、删除和取堆顶数据等操作的具体实现。通过这些内容,读者可以全面了解堆的原理和应用。
104 16
|
3月前
|
存储 安全 数据库
除了 HashMap,还有哪些数据结构可以实现键值对存储?
【10月更文挑战第11天】 除了`HashMap`,其他常见支持键值对存储的数据结构包括:`TreeMap`(基于红黑树,键有序)、`LinkedHashMap`(保留插入顺序)、`HashTable`(线程安全)、`B-Tree`和`B+Tree`(高效存储大量数据)、`SkipList`(通过跳跃指针提高查找效率)及`UnorderedMap`(类似`HashMap`)。选择合适的数据结构需根据排序、并发、存储和查找性能等需求。
|
4月前
|
存储 Java
java数据结构,线性表链式存储(单链表)的实现
文章讲解了单链表的基本概念和Java实现,包括头指针、尾节点和节点结构。提供了实现代码,包括数据结构、接口定义和具体实现类。通过测试代码演示了单链表的基本操作,如添加、删除、更新和查找元素,并总结了操作的时间复杂度。
java数据结构,线性表链式存储(单链表)的实现
|
3月前
|
存储 编译器 C++
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
【初阶数据结构】掌握二叉树遍历技巧与信息求解:深入解析四种遍历方法及树的结构与统计分析
|
4月前
|
存储 JSON NoSQL
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
这篇文章是关于Redis基本数据结构的学习笔记,包括了String、Hash、Set、List和SortedSet的介绍和常用命令。文章解释了每种数据结构的特点和使用场景,并通过命令示例演示了如何在Redis中操作这些数据结构。此外,还提供了一些练习示例,帮助读者更好地理解和应用这些数据结构。
redis基本数据结构(String,Hash,Set,List,SortedSet)【学习笔记】
|
3月前
探索顺序结构:栈的实现方式
探索顺序结构:栈的实现方式
|
4月前
|
存储 人工智能 C语言
数据结构基础详解(C语言): 栈的括号匹配(实战)与栈的表达式求值&&特殊矩阵的压缩存储
本文首先介绍了栈的应用之一——括号匹配,利用栈的特性实现左右括号的匹配检测。接着详细描述了南京理工大学的一道编程题,要求判断输入字符串中的括号是否正确匹配,并给出了完整的代码示例。此外,还探讨了栈在表达式求值中的应用,包括中缀、后缀和前缀表达式的转换与计算方法。最后,文章介绍了矩阵的压缩存储技术,涵盖对称矩阵、三角矩阵及稀疏矩阵的不同压缩存储策略,提高存储效率。
495 8
|
3月前
|
存储 算法
【数据结构】二叉树——顺序结构——堆及其实现
【数据结构】二叉树——顺序结构——堆及其实现
|
3月前
|
存储
【初阶数据结构】深入解析循环队列:探索底层逻辑
【初阶数据结构】深入解析循环队列:探索底层逻辑
|
4月前
|
存储 Java
java数据结构,线性表顺序存储(数组)的实现
文章介绍了Java中线性表顺序存储(数组)的实现。线性表是数据结构的一种,它使用数组来实现。文章详细描述了线性表的基本操作,如增加、查找、删除、修改元素,以及其他操作如遍历、清空、求长度等。同时,提供了完整的Java代码实现,包括MyList接口和MyLinearList实现类。通过main函数的测试代码,展示了如何使用这些方法操作线性表。