详解循环队列

1,802 阅读8分钟

详解循环队列

定义

循环队列是一种特殊的队列,它使用有限的存储空间来实现队列的操作。循环队列将队列的尾部和头部相连,形成一个环状的逻辑空间,使得队列的头部和尾部可以循环使用存储空间。在循环队列中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。

特点

  1. 高效利用存储空间,通过循环利用存储空间来减少浪费。
  2. 防止伪溢出,循环队列的设计可以避免伪溢出的发生。
  3. 固定的队列大小,循环队列的大小是固定的,不能动态调整。
  4. 队头和队尾指针,循环队列需要维护两个指针来跟踪队头和队尾元素的位置。
  5. 先进先出(FIFO),循环队列遵循先进先出原则,先进入的元素先出队。
  6. 时间复杂度,循环队列的插入和删除操作的时间复杂度为O(1)。

队列的运算

初始化

这段代码是C语言中循环队列的初始化函数,它接收一个指向循环队列结构体的指针作为参数。在函数体中,将队列的头指针和尾指针都初始化为0,表示队列初始时为空。函数返回值为0,表示函数执行成功。

/*循环队列初始化*/
int init(CirclesQueue *Q)
{
	Q->front = Q->rear = 0;
	return 0;
}

入队

这段代码是C语言中循环队列的入队操作函数。它首先检查队列是否已满,如果已满则输出错误信息并返回10003,如果队列未满,更新队列的 "rear" 指针(它指示队列的尾部),并将元素添加到队列的尾部。

/*入队*/
int enqueue(CirclesQueue *Q, DataType x)
{
	if(isfull(Q))
	{
		printf("队列已满!10003\n");
		return 10003;
	}

	Q->rear = (Q->rear+1) % MAXSIZE;
	Q->data[Q->rear] = x;
	
	return 0;
}

出队

这段代码是C语言中循环队列的出队操作函数。它首先检查队列是否为空,如果为空则输出错误信息并返回10003,如果非空它会将队列的第一个元素删除(移动头指针),并将它赋值给x指向的内存位置。并返回0。

/*出队*/
int dequeue(CirclesQueue *Q, DataType *x)
{
	if(isempty(Q))
	{
		printf("队列为空!10003\n");
		return 10003;
	}
	Q->front = (Q->front+1) % MAXSIZE;
	*x = Q->data[Q->front];
	return 0;
}

队空?

这段代码是C语言中判断循环队列是否为空的函数。如果循环队列的头指针和尾指针相等,则队列为空,函数返回1;否则,队列不为空,函数返回0。

/*队空*/
int isempty(CirclesQueue *Q)
{
	return (Q->front == Q->rear) ? 1 : 0;
}

队满

这段代码是C语言中判断循环队列是否已满的函数。如果循环队列的尾指针加1后对最大容量取模等于头指针,则队列已满,函数返回1;否则,队列未满,函数返回0。

/*队满?*/
int isfull(CirclesQueue *Q)
{
	return (Q->rear+1)%MAXSIZE == Q->front ? 1 : 0;
}

队内元素个数

这个函数计算循环队列中的元素数量。声明了一个名为size的整数变量,并将其初始化为0。如果尾部指针在头部指针前面,它就计算两者之间的距离;如果尾部指针在头部指针后面,它就计算最大容量减去两者之间的距离。这样,它就可以正确地计算出队列中的元素数量。

/*队内元素个数*/
int getsize(CirclesQueue *Q)
{
	int size = 0; 
	if(Q->rear > Q->front){
		printf("%d ",Q->rear - Q->front);
		size = Q->rear - Q->front;
	} else if(Q->rear < Q->front){
		printf("%d ",MAXSIZE - (Q->front - Q->rear));
		size = MAXSIZE - (Q->front - Q->rear);
	}
	return size;	
}

队首元素

这段代码是C语言中获取循环队列头部的函数。如果队列为空,则输出错误信息并返回10003;否则,它会计算出一个队首的位置,并取出该位置的元素值(front+1) % MAXSIZE,将其赋值给x指向的内存位置。然后返回0。

/*队首元素*/ 
int getfront(CirclesQueue *Q, DataType *x)
{
	if(isempty(Q)){
		printf("队列为空!10003\n");
		return 10003;
	}
	int i;
	i = (Q->front+1) % MAXSIZE;
	*x = Q->data[i];
	return 0;
}

输出队列

这段代码是C语言中打印循环队列中所有元素的函数。如果队列为空,则输出错误信息;否则,设置 i 为队首的位置。使用 do-while 循环结构,先打印当前位置的元素值,然后将 i 增加1(但不超过 MAXSIZE),并对 MAXSIZE 取模,这样就可以循环地遍历整个队列,当 i 等于队列尾部位置时,跳出循环。

/*输出队列*/
void print(CirclesQueue *Q)
{
	int i;
	if(isempty(Q))
	{
		printf("队列为空!\n");
		return; 
	}
	
	printf("队内所有元素为:");
	i = (Q->front) % MAXSIZE;
	do{
		printf(" %d", Q->data[(i + 1) % MAXSIZE]);
		i = (i + 1) % MAXSIZE;
	} while (i != Q->rear);
	printf("\n");
}

队列的实现

完整代码

项目结构

main.c
CirclesQueue.c
CirclesQueue.h

项目文件

main.c

#include <stdio.h>
#include "CirclesQueue.h"


int main(int argc, char* argv[])
{
	CirclesQueue Q;
	DataType x;
	int cmd;
	char yn;


	do
	{	
		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(" 0. 退出\n");
		printf(" 请选择(0~9):");
		scanf("%d",&cmd);
		switch(cmd)
		{
		case 1:
			init(&Q);
			printf("队列已初始化!\n");
			break;
		case 2:
			printf("请输入要入队的元素x=");
			scanf("%d", &x);
			if(!enqueue(&Q,x))
			{
				printf("元素x=%d已入队\n", x);
			}
			break;
		case 3:
			printf("确定要出队(出队会将删除对首元素, y or n, n)?");
//			flushall();
			getchar();
			scanf("%c", &yn);

			if(yn == 'y' || yn == 'Y')
			{
				if(!dequeue(&Q,&x))
				{
					printf("队首元素【%d】已出队!\n", x);
				}
			}
			break;
		case 4:
			if(isempty(&Q)){
				printf("队列为空,还可以添加元素哦!\n");
				break;
			}
			printf("队列有元素\n"); 
			break;
		case 5:
			if(isfull(&Q)){
				printf("满了哦!可以选择出队哦!\n");
				break;
			}
			printf("队列还未满,还可以添加元素哦!\n"); 
			break;
		case 6:
			printf("队列长度为:%d \n",getsize(&Q)); 
			break;
		case 7:
//			getfront(&Q); 
			if(!getfront(&Q, &x)){
				printf("队首元素为:%d\n", x);
			}
			break;
		case 8:
			print(&Q);
			break;
		case 9:
		    printf("本程序为顺序栈的演示程序,由付雄宇靓仔设计开发。\n对该文章有疑问欢迎交流!\n");
		}

	}while(cmd!=0);


	return 0;
}

CirclesQueue.c

/*
	CirclesQueue.c
*/
#include "CirclesQueue.h"

/*循环队列初始化*/
int init(CirclesQueue *Q)
{
	Q->front = Q->rear = 0;
	return 0;
}


/*入队*/
int enqueue(CirclesQueue *Q, DataType x)
{
	if(isfull(Q))
	{
		printf("队列已满!10003\n");
		return 10003;
	}

	Q->rear = (Q->rear+1) % MAXSIZE;
	Q->data[Q->rear] = x;
	
	return 0;
}

/*队满?*/
int isfull(CirclesQueue *Q)
{
	return (Q->rear+1)%MAXSIZE == Q->front ? 1 : 0;
}


/*出队*/
int dequeue(CirclesQueue *Q, DataType *x)
{
	if(isempty(Q))
	{
		printf("队列为空!10003\n");
		return 10003;
	}
	Q->front = (Q->front+1) % MAXSIZE;
	*x = Q->data[Q->front];
	return 0;
}

/*队空*/
int isempty(CirclesQueue *Q)
{
	return (Q->front == Q->rear) ? 1 : 0;
}

/*队内元素个数*/
int getsize(CirclesQueue *Q)
{
	int size = 0; 
	if(Q->rear > Q->front){
		printf("%d ",Q->rear - Q->front);
		size = Q->rear - Q->front;
	} else if(Q->rear < Q->front){
		printf("%d ",MAXSIZE - (Q->front - Q->rear));
		size = MAXSIZE - (Q->front - Q->rear);
	}
	return size;	
}

/*队首元素*/ 
int getfront(CirclesQueue *Q, DataType *x)
{
	if(isempty(Q)){
		printf("队列为空!10003\n");
		return 10003;
	}
	int i;
	i = (Q->front+1) % MAXSIZE;
	*x = Q->data[i];
	return 0;
}


/*输出队列*/
void print(CirclesQueue *Q)
{
	int i;
	if(isempty(Q))
	{
		printf("队列为空!\n");
		return; 
	}
	
	printf("队内所有元素为:");
	i = (Q->front) % MAXSIZE;
	do{
		printf(" %d", Q->data[(i + 1) % MAXSIZE]);
		i = (i + 1) % MAXSIZE;
	} while (i != Q->rear);
	printf("\n");
}

CirclesQueue.h

/*
	CirclesQueue.h
	循环队列
*/

#define MAXSIZE 5

typedef int DataType;

typedef struct
{
	DataType data[MAXSIZE];
	int front;
	int rear;
}CirclesQueue;

/*循环队列初始化*/
int init(CirclesQueue *Q);

/*入队*/
int enqueue(CirclesQueue *Q, DataType x);

/*队满?*/
int isfull(CirclesQueue *Q);

/*出队*/
int dequeue(CirclesQueue *Q, DataType *);

/*队空*/
int isempty(CirclesQueue *Q);

/*队内元素个数*/
int getsize(CirclesQueue *Q);

/*队首元素*/ 
int getfront(CirclesQueue *Q, DataType *x);

/*输出队列*/
void print(CirclesQueue *Q); 

运行结果

QQ截图20231026233027.png

QQ截图20231026233454.png

小结

队列是一种重要的数据结构,具有先进先出(FIFO)的特性,常用于多线程编程中解决线程同步问题。队列的应用场景非常广泛,包括但不限于消息通讯、异步处理、应用解耦、流量削锋、日志处理、事件驱动的系统、任务调度、实时数据处理以及缓存系统等

循环队列使用数组实现,并能够循环利用空间。在循环队列中,队尾和队头之间形成了一个循环,当队尾指针追上队头指针时,队列不再增长。

循环队列的入队和出队操作是这样的:入队操作会将元素插入到队尾指针所指向的位置,并将队尾指针后移。当队列满时,入队操作会失败。出队操作会删除队头元素,并将队头指针后移。当队列为空时,出队操作会失败。队空和队满的条件是:当队列为空时,队头指针和队尾指针同时指向相同的位置;队列满的条件是队尾指针加1等于队头指针。循环队列需要预留一个空间来区分队列为空和队列满的状态,所以队列满的条件是队尾指针加1取模等于队头指针。

参考文献

文心一言