队列的定义
队列是一种特殊的线性表,只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
循环队列的优缺点
循环队列的优点包括:
- 空间利用率高:由于循环队列是环形的,可以利用数组中未使用的空间,避免出现队列溢出的情况,从而使得空间得以充分利用。
- 入队、出队速度快:在循环队列中,入队时只需要在队尾追加元素,出队时只需要删除队头元素,不需要移动大量元素,因此速度快。
循环队列的缺点是在判断队列为空或满时,可能会出现误判。当尾指针追上头指针时,无法直接判断队列为空还是满。为了解决这个问题,可以采用一些额外的判断条件,例如判断头尾指针之差是否大于等于一定阈值,或者在队列中额外设置一个标志位等。(图片网上找的)
循环队列的基本运算
1.初始化队列
2.入队
3.出队
4.队列是否已满
5.队列是否为空
6.遍历队列
7.取队头元素
8.取队尾元素
9.按下标获取队列元素
10.销毁队列元素
11.查询队列有多少个元素
12.帮助
1.初始化队列
队列首先是有队头队尾组成还有一个data数组,我们在初始化化的时候,先给队头和队尾的值赋值0
//初始化队列
int initQueue(Queue *Q){
Q->front=Q->rear=0;
return 0;
}
运行截图
2.入队
入队操作主要是队尾的值除以队列的容量在取余,先判断队列容量是否已满(这个后面再解释),之后在把队尾的值当做data元素的下标,再给队尾赋值,我这里主要入队4个元素 22 33 44 55以便于做之后的测试
//入队
int enQueue(Queue *Q,DataType value){
if(isFull(Q)==0){
printf("队列已满![10001]\n");
return 10001;
}
Q->rear=(Q->rear+1)%MAXSIZE;
Q->data[Q->rear]=value;
return 0;
}
//这段是代码执行的条件
case 2:
printf("请输入你要入队的元素\n");
scanf("%d",&value);
queueStatus=enQueue(&Q,value);
//自己封装一个颜色的函数库
treeColor(queueStatus);
if(queueStatus==0){
printf("元素%d入队成功\n",value);
}
break;
运行截图
3.出队
出队操作主要是队头+1取余在把要出队的元素地址赋值给队头,先判断队列是否为null,简单点解释就是删除队头元素.
//出队
int deQueue(Queue *Q,DataType *value){
if(isEmpty(Q)==1){
printf("队列为null! [10002]\n");
return 10002;
}
Q->front=(Q->front+1)%MAXSIZE;
*value=Q->data[Q->front];
return 0;
}
//这段代码是执行条件
case 3:
printf("是否要出队,该操作会删除头元素,不可恢复(y确定n取消)\n");
scanf("%s",&yn);
if(yn=='Y'||yn=='y'){
queueStatus=deQueue(&Q,&value);
if(queueStatus==0){
printf("元素");
treeColor(queueStatus);
printf(" %d ",value);
initColor();
printf("已出队\n");
}
}
break;
代码执行效果
因为之前添加的元素是22 33 44 55,所以符合队列先进先出 22出来
4.队列是否已满
这个比较简单,直接使用队列的尾+1除以容量是否等于队列的头,如果等于那就代表满了,没有满就返回容量,以下代码有求队列个数后面再解释
//判断队列是否已满
int isFull(Queue *Q){
return (Q->rear+1)%MAXSIZE==Q->front?0:1;
}
这段是代码执行条件
case 4:
queueStatus=isFull(&Q);
//获取队列元素个数
int queueCount=getQueueCount(&Q);
treeColor(queueStatus);
if(queueStatus==0){
printf("队列已满\n");
}else{
printf("队列还有%d的容量\n",MAXSIZE-queueCount);
}
break;
代码执行效果
因为22已经出队,所以容量是容量(100)-元素个数(3)=97
5.队列是否为空
这个直接判断队头是否等于队尾,等于就是空了,或者队头和队尾是否等于0,都行,我这里是用第一种。
//判断队列是否为null
int isEmpty(Queue *Q){
return Q->front==Q->rear?1:0;
}
这里是代码执行的条件
case 5:
queueStatus=isEmpty(&Q);
treeColor(queueStatus);
if(queueStatus==1){
printf("队列为null\n");
}else{
printf("队列有元素\n");
}
break;
代码执行效果
因为我之前添加了 22 33 44 55, 22元素已经出队还有3个,所以提示队列有元素
6.遍历队列
这个实现逻辑就是使用循环,首先先判空,之后在定义一个指针i(不是C语言指针)和一个指针j,i指针指向队头,j指针指向队尾,循环条件是 i不等于j,依次输出队列元素,之后 i 就等于i+1取余也可以直接i++,我这使用的是i+1取余。
//遍历队列
void printQueue(Queue *Q){
if(isEmpty(Q)==1){
treeColor(10008);
printf("队列为null\n");
return;
}
int i=Q->front%MAXSIZE;
int j=Q->rear;
while(i!=j){
//白色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
printf("队列第%d个元素是:",i+1);
//黑色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),2);
printf("[%d]\n",Q->data[i+1%MAXSIZE]);
i=(i+1)%MAXSIZE;
}
}
代码执行效果
因为之前添加的元素 22 33 44 55, 22已经出队,所以输出 33 44 55
7.去队头元素
取头队头元素直接把队头+1当下标,也可以在求个余数,我这里是取余,当然要先判空。
//取对头元素
int getFront(Queue *Q){
if(isEmpty(Q)==1){
printf("队列为null! [100010]\n");
return 10002;
}
return Q->data[(Q->front+1)%MAXSIZE];
}
这里是执行代码的条件
case 7:
printf("队头元素为: ");
treeColor(0);
printf("%d\n",getFront(&Q));
break;
代码执行效果
因为22 已经出队,元素就是 33 44 55
8.取队尾元素
这个跟取队头元素逻辑是一样的,直接上代码
//取对尾元素
int getRear(Queue *Q){
if(isEmpty(Q)==1){
printf("队列为null! [100012]\n");
return 10003;
}
return Q->data[Q->rear];
}
执行效果
22已经出队,元素还剩 33 44 55
9.按位置获取元素
按位置获取元素实际上就是按数组下标获取元素,当输入的下标在队列范围内就可以获取实现逻辑就是先判断下标是否合法(在队列范围内),在把输入的下标当做元素的位置。
//按下标获取元素
int getValueByIndex(Queue *Q,int index){
//如果大于队列元素的个数代表不合法
if(index>getQueueCount(Q)){
return 10004;
}
return Q->data[index%MAXSIZE];
}
这里是执行代码的条件
case 9:
printf("请输入你要获取元素的下标: \n");
scanf("%d",&num);
queueStatus=getValueByIndex(&Q,num);
if(queueStatus!=10004){
printf("根据您的索引获取到的元素为:");
treeColor(0);
printf("%d\n",queueStatus);
}else{
treeColor(3);
printf("您输入的下标不合法\n");
}
break;
10.销毁队列
因为队列是用数组存储的,数组的长度是固定的,所以没有在物理上销毁,只在逻辑上销毁,直接把队头和队尾赋值为0,我这边销毁前先遍历了一遍。
//销毁队列元素
int destroyQueueValue(Queue *Q){
if(isEmpty(Q)==1){
treeColor(10008);
printf("队列没有元素,不需要销毁\n");
return 10008;
}
int i=Q->front;
int j=Q->rear;
while(i!=j){
//白色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
printf("队列第%d个元素:",i+1);
//黑色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
printf("[%d]已被销毁\n",Q->data[i+1]);
i++;
}
Q->front=Q->rear=0;
return 0;
}
这里是代码执行条件
case 10:
printf("确定要销毁队列元素(y确定,n取消)?\n");
scanf("%s",&yn);
if(yn=='y'||yn=='Y'){
destroyQueueValue(&Q);
}
break;
代码执行结果
22出队了,所以只有 33 44 55三个元素,之后再遍历一遍显示队列为空
11.查询队列元素个数
实现逻辑就是队尾减去队头
int getQueueCount(Queue *Q){
return (Q->rear-Q->front);
}
这里是代码执行条件
case 11:
queueStatus=getQueueCount(&Q);
if(queueStatus!=0){
printf("队列元素还有 ");
treeColor(0);
printf("%d ",queueStatus);
initColor();
printf("个\n");
}else{
treeColor(2);
printf("队列还没有元素\n");
}
break;
代码执行效果
因为在之前已经销毁队列了,所以显示队列没有元素
入队 22 33 44 55 是个元素
12.帮助
本次作业循环队列有季老师指导,丁俊冉完成
4.整体代码
1.tree.h
自定义封装的函数,封账树的图案已经颜色的调整
#include <windows.h>
int tree(){
int i, j, n, b, s;
printf("1.深蓝色 2.深绿色 3.深蓝绿色 4.深红色 5.紫色\n");
printf("请输入树的层数以及你喜欢的颜色:");
scanf("%d", &n);
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),n);
for (i = 1; i <= n; i++)
{
for (j = 1; j <= i + 1; j++)
{
for (s = 1; s <= n + 1 - j; s++)
{
printf(" ");
}
for (s = 1; s <= 2 * j - 1; s++)
{
if(s==1){
printf("丁");
}else if(s==2){
printf("俊");
}else{
printf("冉");
}
}
printf("\n");
}
}
for (b = 1; b <= n * 2; b++)
for (s = 1; s <= n * 2; s++)
{
if (s == n * 2)
{
printf("|三|\n");
continue;
}
printf(" ");
}
printf("\n");
}
treeColor(int n){
if(n==0){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),2);
}else{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),4);
}
}
initColor(){
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
}
2.Queue.h
封装着队列操作的核心函数以及结构体
#include <windows.h>
#define MAXSIZE 100
typedef int DataType;
typedef struct{
DataType data[MAXSIZE];
int front;
int rear;
} Queue;
//初始化队列
int initQueue(Queue *Q);
//入队
int enQueue(Queue *Q,DataType value);
//判断队列是否已满
int isFull(Queue *Q);
//出队
int deQueue(Queue *Q,DataType *value);
//判断队列是否为空
int isEmpty(Queue *Q);
//遍历队列
void printQueue(Queue *Q);
//获取对列元素个数
int getQueueCount(Queue *Q);
//取对头元素
int getFront(Queue *Q);
//取对尾元素
int getRear(Queue *Q);
//按下标获取元素
int getValueByIndex(Queue *Q,int index);
//销毁队列元素
int destroyQueueValue(Queue *Q);
3.Queue.c
实现Queue.h队列的核心函数
#include "Queue.h"
int destroyQueueValue(Queue *Q){
if(isEmpty(Q)==1){
treeColor(10008);
printf("队列没有元素,不需要销毁\n");
return 10008;
}
int i=Q->front;
int j=Q->rear;
while(i!=j){
//白色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
printf("队列第%d个元素:",i+1);
//黑色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),3);
printf("[%d]已被销毁\n",Q->data[i+1]);
i++;
}
Q->front=Q->rear=0;
return 0;
}
//按下标获取元素
int getValueByIndex(Queue *Q,int index){
//如果大于队列元素的个数代表不合法
if(index>getQueueCount(Q)){
return 10004;
}
return Q->data[index%MAXSIZE];
}
//取对头元素
int getFront(Queue *Q){
if(isEmpty(Q)==1){
printf("队列为null! [100010]\n");
return 10002;
}
return Q->data[(Q->front+1)%MAXSIZE];
}
//取对尾元素
int getRear(Queue *Q){
if(isEmpty(Q)==1){
printf("队列为null! [100012]\n");
return 10003;
}
return Q->data[Q->rear];
}
//获取队列元素个数
int getQueueCount(Queue *Q){
return (Q->rear-Q->front);
}
//初始化队列
int initQueue(Queue *Q){
Q->front=Q->rear=0;
return 0;
}
//入队
int enQueue(Queue *Q,DataType value){
if(isFull(Q)==0){
printf("队列已满![10001]\n");
return 10001;
}
Q->rear=(Q->rear+1)%MAXSIZE;
Q->data[Q->rear]=value;
return 0;
}
//出队
int deQueue(Queue *Q,DataType *value){
if(isEmpty(Q)==1){
printf("队列为null! [10002]\n");
return 10002;
}
Q->front=(Q->front+1)%MAXSIZE;
*value=Q->data[Q->front];
return 0;
}
//判断队列是否为null
int isEmpty(Queue *Q){
return Q->front==Q->rear?1:0;
}
//判断队列是否已满
int isFull(Queue *Q){
return (Q->rear+1)%MAXSIZE==Q->front?0:1;
}
//遍历队列
void printQueue(Queue *Q){
if(isEmpty(Q)==1){
treeColor(10008);
printf("队列为null\n");
return;
}
int i=Q->front%MAXSIZE;
int j=Q->rear;
while(i!=j){
//白色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),7);
printf("队列第%d个元素是:",i+1);
//黑色字体
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE),2);
printf("[%d]\n",Q->data[i+1%MAXSIZE]);
i=(i+1)%MAXSIZE;
}
}
4.main.c
主函数,执行所有代码的核心文件
#include <stdio.h>
#include <stdlib.h>
#include "Queue.c"
#include "tree.h"
int main(int argc, char *argv[]) {
tree();
int cmd;
int num;
char yn;
Queue Q;
//队列函数返回值
int queueStatus;
//元素
DataType value;
do
{
//初始化颜色
initColor();
printf("---------队列演示程序-----------\n");
printf(" 1. 初始化\n");
printf(" 2. 入队\n");
printf(" 3. 出队\n");
printf(" 4. 队列是否已满?\n");
printf(" 5. 队列是否为空?\n");
printf(" 6. 遍历队列\n");
printf(" 7. 取队头元素\n");
printf(" 8. 取队尾元素\n");
printf(" 9. 按下标获取队元素\n");
printf(" 10.销毁队列元素\n");
printf(" 11.查询队列多少个元素\n");
printf(" 12.帮助\n");
printf("请选择(0~9,0退出):");
scanf("%d", &cmd);
switch(cmd)
{
case 1:
queueStatus=initQueue(&Q);
//自己封装的函数0代表成功绿色1代表失败 红色
treeColor(queueStatus);
if(queueStatus==0){
printf("初始化成功\n");
}
break;
case 2:
printf("请输入你要入队的元素\n");
scanf("%d",&value);
queueStatus=enQueue(&Q,value);
//自己封装一个颜色的函数库
treeColor(queueStatus);
if(queueStatus==0){
printf("元素%d入队成功\n",value);
}
break;
case 3:
printf("是否要出队,该操作会删除头元素,不可恢复(y确定n取消)\n");
scanf("%s",&yn);
if(yn=='Y'||yn=='y'){
queueStatus=deQueue(&Q,&value);
if(queueStatus==0){
printf("元素");
treeColor(queueStatus);
printf(" %d ",value);
initColor();
printf("已出队\n");
}
}
break;
case 4:
queueStatus=isFull(&Q);
//获取队列元素个数
int queueCount=getQueueCount(&Q);
treeColor(queueStatus);
if(queueStatus==0){
printf("队列已满\n");
}else{
printf("队列还有%d的容量\n",MAXSIZE-queueCount);
}
break;
case 5:
queueStatus=isEmpty(&Q);
treeColor(queueStatus);
if(queueStatus==1){
printf("队列为null\n");
}else{
printf("队列有元素\n");
}
break;
case 6:
printQueue(&Q);
break;
case 7:
printf("队头元素为: ");
treeColor(0);
printf("%d\n",getFront(&Q));
break;
case 8:
printf("队尾元素为:");
treeColor(0);
printf("%d\n",getRear(&Q));
break;
case 9:
printf("请输入你要获取元素的下标: \n");
scanf("%d",&num);
queueStatus=getValueByIndex(&Q,num);
if(queueStatus!=10004){
printf("根据您的索引获取到的元素为:");
treeColor(0);
printf("%d\n",queueStatus);
}else{
treeColor(3);
printf("您输入的下标不合法\n");
}
break;
case 10:
printf("确定要销毁队列元素(y确定,n取消)?\n");
scanf("%s",&yn);
if(yn=='y'||yn=='Y'){
destroyQueueValue(&Q);
}
break;
case 11:
queueStatus=getQueueCount(&Q);
if(queueStatus!=0){
printf("队列元素还有 ");
treeColor(0);
printf("%d ",queueStatus);
initColor();
printf("个\n");
}else{
treeColor(2);
printf("队列还没有元素\n");
}
break;
case 12:
printf("本次作业循环队列有季老师指导,丁俊冉完成\n");
break;
}
}while(cmd!=0);
return 0;
}
5.小结
队列是一种先进先出(FIFO)的数据结构,它允许在一端添加元素,在另一端删除元素。队列中的元素按照添加的顺序进行排列,最早添加的元素将首先被删除。队列常用于需要按照顺序处理元素的场景,例如在处理用户请求或进行任务调度等。
6.参考文献
百度
季老师代码
文心一言