数据结构——循环队列的设计及函数实现(C语言)

一、什么是循环队列

1.队列

(1)队列是只允许在一端 进行插入操作,而在另一端进行删除操作的线性表。

(2)特点:先进先出FIFO:First In First Out)

(3)允许插入的一端称为队尾 ,允许删除的一端称为队头

(4)同样是线性表,队列也有类似线性表的各种操作,不同的就是插入数据只能在队尾进行删除数据只能在队头进行

队列的插入操作,一般称为入队 ;队列的删除操作,一般称为出队

2.循环队列

(1)队列的顺序存储结构

线性表有顺序存储和链式存储,队列作为一种特殊的线性表,同样存在这两种存储方式。

队列的链式存储结构链队列

数据结构------链队列的设计及函数实现(C语言)-CSDN博客https://blog.csdn.net/wy_05136/article/details/160090401?spm=1001.2014.3001.5501队列的顺序存储结构 :有一组地址连续的存储单元依次存放从队头到队尾的元素。

我们假设一个队列有n个元素,则顺序存储的队列需建立一个大于n的数组,并把队列的所有元素存储在数据的前n个单位,数组的下标为0的一端即是队头。

(2)设计顺序存储的队列------循环队列

对于设计顺序存储的队列,我们需要解决4个问题:

(1)所谓的入队操作,其实就是在队尾追加一个元素,不需要移动任何元素,因此时间复杂度为O(1);但是,出队操作是删除队头元素,这意味着,队列中的所有元素都要向前移动一位,此时时间复杂度为O(n)。

问题一:如何对顺序表进行改造,让其插入和删除的时间复杂度都达到O(1)?

解决方法 :我们设置两个指针frontrear分别指示队头元素和队尾元素的位置。

注意 :我们通常默认front指针 指向第一个元素的位置;rear指针 指向最后一个元素的下一个位置

队列为空 时,front == rear

这样,当我们进行出队操作时,我们不用移动队列中的元素,只需要将front指针往后移动一位,把下一个元素当作队头即可。

(2)我们假设一个长度为5的数组,其中存放一个元素个数为4的队列,front指针指向第一个元素a1,rear指针指向最后一个元素a4的下一个位置。

首先,我们进行出队操作,rear指针不变,front指针往后移动一位,指向a2;

接着,我们进行入队操作,front指针不变,rear指针往后移动一位。

此时我们发现,rear指针移动到了数组之外,会造成内存泄露;

此外,此时数组末尾已经没有空闲位置,如果再次进行入队操作的话,就会产生数组越界 的错误,但是,实际上我们的队列在0下标的位置还是空闲的,这个数组我们并没有充分利用,造成内存浪费。我们把这种现象叫做"假溢出"。

问题二:我们如果将出队时所留下的空闲位置再次利用上?

解决方法 :当末尾满了,我们让它再从头开始,也就是让队列头尾相接

我们把队列的这种头尾相接的顺序存储结构 称为循环队列


如何实现让rear指针从头开始?

rear = (rear+1) % MAXSIZE;

MAXSIZE:队列的最大长度


此方法,虽然可以可以将空闲位置利用上,但是,也意味着我们无法对数组进行扩容处理

所以,当我们选择使用循环队列是,必须为它设定一个最大队列长度

若用户无法预估所用队列的最大长度,则最好采用链队列。

(3)继续上述的例子,我们入队a5,front指针不变,rear指针指向0下标位置(rear = (4+1) % 5 = 0);

接着,我们入队a6,front指针不变,rear指针往后移动一位(rear = (0+1) % 5 = 1)。

我们前面提到,空队列时,front == rear;

此时,队列满时,我们发现 front == rear也成立。

问题三:由于队列头尾相连,导致判空和判满条件冲突,如何区分开?

解决方法一:加一个成员变量Size,用于保存当前元素个数;Size == 0时,队列为空,Size != 0 && rear == front时,队列为满。

解决方法二(常用) :我们在队列尾部保留一个元素空间。这样的话,当数组中还剩一个空闲位置,此时我们就认为队列已满。判满条件:rear指针再走一步就遇到front指针( (rear+1) % MAXSIZE == front)。

也就是说,当出现下图情况时( (rear+1) % MAXSIZE == (0+1) % 5 == 1 == front),我们就认为队列已满,不再进行入队操作。

(4)我们发现,当rear > front时,队列的长度为rear-front;

但当rear < front时,队列的长度分为两段,一段是MAXSIZE-front,另一端是rear-0,加在一起,队列的长度为rear-front+MAXSIZE

问题四:如何用一个通用公式求队列的有效长度?

解决方法:Size = (rear-front+MAXSIZE) % MAXSIZE

二、循环队列结构体设计及函数实现(C语言)

(一)循环队列结构体设计

1.用一组地址连续的存储单元(通常用数组):依次存放从队头到队尾的元素

2.front指针:指向队头元素的位置

3.rear指针:指向队尾元素的下一个位置

cpp 复制代码
#include <assert.h>
#include <stdlib.h>
#include <stdio.h>

#define MAXQSIZE 10

typedef int ElemType;

//循环队列的结构体设计
typedef struct CircleQueue {
    //1.指针类型:用来接收malloc的返回值
	ElemType* arr;

    //2.队头指针,指向第一个元素
	int front; 

    //3.队尾指针,指向最后一个元素的下一个位置
	int rear; 

}CircleQueue;

(二)链队列的C语言函数实现

1.初始化

cpp 复制代码
void Init_CircleQueue(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.动态分配队列的底层数组内存
	pcq->arr = (ElemType*)malloc(MAXQSIZE * sizeof(ElemType));
	if (pcq->arr == NULL) exit(EXIT_FAILURE); //内存分配失败

    //2.front == rear == 0,表示队列为空
	pcq->front = 0;
	pcq->rear = 0;
}

2.判空

cpp 复制代码
bool Empty(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.若front == rear,说明队列为空,返回true;
    //  否则,返回false
	return pcq->front == pcq->rear;
}

3.判满

cpp 复制代码
bool Full(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.若rear指针再走一步就遇到front指针,说明队列已满,返回true;
    //  否则,返回false
	return (pcq->rear + 1) % MAXQSIZE == pcq->front;
}

4.入队

cpp 复制代码
bool Push(CircleQueue* pcq, ElemType val) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

	//1.判满(如果满了,则无法进行入队操作,直接返回)
	if (Full(pcq)) {
		printf("队列已满,无法入队\n");
		return true;
	}

    //2.直接给rear下标的位置插入val
	pcq->arr[pcq->rear] = val;

    //3.rear指针往后挪动一位
	pcq->rear = (pcq->rear + 1) % MAXQSIZE;

	return true;
}

5.出队

cpp 复制代码
bool Pop(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

	//1.判空
	assert(!Empty(pcq));

    //2.直接将front指针往后挪动一位
	pcq->front = (pcq->front + 1) % MAXQSIZE;

	return true;
}

6.获取队头元素值

cpp 复制代码
ElemType Front(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

	//1.判空,若队列为空,则不存在队头元素
	assert(!Empty(pcq));

    //2.直接返回队头元素的值
	return pcq->arr[pcq->front];
}

7.获取队列有效元素个数

详细解释可见前面的问题四。

cpp 复制代码
int Get_Size(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

	return (pcq->rear - pcq->front + MAXQSIZE) % MAXQSIZE;
}

8.打印

cpp 复制代码
void Show(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.从front指针开始,到rear指针结束,依次打印
	for (int i = pcq->front; i != pcq->rear; i = (i + 1) % MAXQSIZE) printf("%d ", pcq->arr[i]);
	printf("\n");
}

9.清空

cpp 复制代码
void Clear(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.直接将队头指针和队尾指针设为0
	pcq->front = 0;
	pcq->rear = 0;
}

10.销毁

cpp 复制代码
void Destroy(CircleQueue* pcq) {
    //0.断言:检查传入的指针是否为空
	assert(pcq != NULL);

    //1.释放内存
	free(pcq->arr);
	pcq->arr = NULL; //避免野指针

    //2.重置状态
	pcq->front = 0;
	pcq->rear = 0;
}

11.主函数测试

cpp 复制代码
int main() {
	CircleQueue queue;

	//初始化
	Init_CircleQueue(&queue);

	//判空
	if (Empty(&queue)) printf("队列为空\n\n");
	else printf("队列未空\n\n");


	//入队+判满
	printf("入队(队列未满):\n");
	Push(&queue, 1);
	Push(&queue, 2);
	Push(&queue, 3);
	Push(&queue, 4);
	Push(&queue, 5);
	Push(&queue, 6);
	Push(&queue, 7);
	Push(&queue, 8);
	Push(&queue, 9);
	Show(&queue);

	printf("\n");

	printf("入队(队列已满):\n");
	Push(&queue, 10);
	Show(&queue);

	printf("\n");

	//出队
	printf("出队:\n");
	Pop(&queue);
	Show(&queue);

	printf("\n");

	//获取队头元素值
	printf("队头元素值为 %d\n", Front(&queue));

	printf("\n");

	//获取队列有效元素个数
	printf("队列有效元素个数为 %d\n", Get_Size(&queue));

	printf("\n");

	//清空+判空
	if (Empty(&queue)) printf("队列为空\n\n");
	else printf("队列未空\n\n");

	Clear(&queue);

	if (Empty(&queue)) printf("队列为空\n\n");
	else printf("队列未空\n\n");

	//销毁
	Destroy(&queue);

	return 0;
}
相关推荐
啊哦呃咦唔鱼2 小时前
leetcode二分查找
数据结构·算法·leetcode
郝学胜-神的一滴2 小时前
[ 力扣 1124 ] 解锁最长良好时段问题:前缀和+哈希表的优雅解法
java·开发语言·数据结构·python·算法·leetcode·散列表
无限进步_2 小时前
二叉树的中序遍历(非递归实现)
开发语言·数据结构·c++·windows·算法·visual studio
计算机安禾2 小时前
【数据结构与算法】第48篇:算法思想(三):贪心算法
c语言·开发语言·数据结构·算法·贪心算法·代理模式·图论
codebrick2 小时前
408 数据结构:快排 / 堆排 / 归并 / 希尔 等排序算法对比(复杂度、稳定性、真题考点
数据结构·考研·算法·排序算法·408
hehelm2 小时前
string的模拟实现
数据结构·算法
自我意识的多元宇宙3 小时前
树与二叉树--二叉树的定义及其主要特性
数据结构
寒秋花开曾相惜12 小时前
(学习笔记)第四章 处理器体系结构
linux·网络·数据结构·笔记·学习
故事和你9112 小时前
洛谷-数据结构1-1-线性表1
开发语言·数据结构·c++·算法·leetcode·动态规划·图论