目录
目录
- 目录
- 队列的概念
- 循环队列的实现
- 1.以数组为基础实现循环队列
- 2.以链表为基础实现循环队列
- 两个栈实现队列
队列的概念
队列(Queue)和栈类似,相同点是都属于线性结构,不同点是栈遵循“后进先出”原则,而队列遵循“先进先出”的原则,也被称为“FIFO”结构,就是“First Input First Output”。
数据结构中的队列的两端都允许操作,只不过要求数据只能从队列的一端插入(入队),从队列的另一端删除(出队),可以把队列理解为一根水管,水管有进水口和出水口。一般把允许数据插入的一端称为队尾(Tail或者Rear),一般把允许删除数据的一端称为队头队首(Head或者Front)。
和栈一样,队列也可以通过数组或者链表来实现,通过数组来实现的队列我们称之为“顺序队列”,通过链表实现的队列称之为“链式队列”
假溢出问题
如果使用数据实现简单的队列,当执行多次入队和出队之后,队列的尾指针 Rear 和头指针 Front 已经到达数组的末尾,即使队列中有许多空闲位置(前面的元素删除后就会有位置),也无法再进行入队操作。这种情况被称为 “假溢出”,也就是队列又是空的又是满的,这看起来是一个自相矛盾的事情却在队列里面出现了,如下图所示:
解决方案:
- 在学习顺序表(数组)的时候,当顺序表(数组)中删除一个元素,那么这个位置之后的所有元素都需要往前移动一位,所以队列中也是同理,假如我们用数组来实现的话,当元素出队时,我们把所有元素都往前移,不过这样因为每次都需要搬移数据,导致入队的时间复杂度是O(n)
- 可以把队列设置为循环队列,循环队列也被称为“环形缓冲区”,通过使队列的尾部与头部相连,形成一个闭环,可以使得出队后的空间得到重新利用,从而最大限度地使用数组空间,避免了空间的浪费,在移动数据的时候只需要移一次即可,所以时间复杂度就是O(1)
循环队列的实现
1.以数组为基础实现循环队列
2.以链表为基础实现循环队列
两个栈实现队列
- // 基本思想:利用栈s1和栈s2实现队列,栈的思想是“先进后出”,队列的思想是“先进先出”,可以选择把栈s1作为入队缓存,栈s2作为出队缓存
复制代码- //代码实现:在栈的基础上使用两个栈实现循环队列的入队和出队
- /**
- * @file name : Stack_Queue.c
- * @brief : 该程序以栈为基础实现循环队列元素的初始化、入队、出队、遍历,另外为了提高可移植性,所以循环队列中
- * 数据元素的类型为DataType_t,用户可以根据实际情况修改循环队列中元素的类型
- * @author : MINDSETT@163.com
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- * CopyRight (c) 2025 MINDSETT@163.com All Right Reserved
- */
- #include <stdbool.h>
- #include <stdlib.h>
- #include <stdio.h>
- #define maxSize 10
- //指定顺序栈中的元素的数据类型,用户可根据需要进行修改
- typedef int DataType_t;
- //构造一个实现顺序栈的各项参数(栈底地址+栈容量+栈顶元素的下标)的结构体
- typedef struct Sequence_Stack
- {
- DataType_t * bottom; //栈底地址
- unsigned int size; //栈容量
- int top; //栈顶元素的下标
- }SeqStack_t;
- /**
- * @name : SeqStack_Create
- * @brief : 创建一个顺序栈并进行初始化
- * @param : None
- * @retval : 返回栈底地址
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- SeqStack_t *SeqStack_Create(unsigned int size)
- {
- //1.利用calloc对顺序栈的管理结构体申请一个内存
- SeqStack_t *manager=(SeqStack_t *)calloc(1,sizeof(SeqStack_t));
- if (NULL == manager){
- perror("calloc memory for manager is failed\n");
- exit(-1);
- }
- //2.对顺序栈管理结构体的所有元素进行初始化
- manager->bottom=(DataType_t *)calloc(size,sizeof(DataType_t));
- if (NULL == manager->bottom){
- perror("calloc memory for Stack is failed\n");
- exit(-1);
- }
- manager->size=size;
- manager->top=-1;
- return manager;
- }
- /**
- * @name : SeqStack_IsFull
- * @brief : 判断顺序栈是否已满
- * @param :
- * @manager:需要判断的顺序栈
- * @retval : 已满返回true,未满返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- bool SeqStack_IsFull(SeqStack_t *manager)
- {
- return (manager->top+1==manager->size)? true : false;
- }
- /**
- * @name : SeqStack_IsEmpty
- * @brief : 判断顺序栈是否为空
- * @param :
- * @manager:需要判断的顺序栈
- * @retval : 为空返回true,不为空返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- bool SeqStack_IsEmpty(SeqStack_t *manager)
- {
- return (-1==manager->top)? true : false;
- }
- /**
- * @name : SeqStack_Push
- * @brief : 入栈
- * @param :
- * @manager:需要操作的顺序栈
- * @Data:需要压入的元素
- * @retval : 成功返回true,失败返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- bool SeqStack_Push(SeqStack_t *manager,DataType_t Data)
- {
- //判断顺序栈是否已满
- if ( SeqStack_IsFull(manager) ){
- printf("Sequence Stack is Full\n");
- return false;
- }
- //如果顺序栈有剩余空间,则把新元素插入顺序栈的栈顶,并更新管理结构体中最后栈顶的元素下标
- manager->bottom[++manager->top]=Data;
- return true;
- }
- /**
- * @name : SeqStack_Pop
- * @brief : 出栈
- * @param :
- * @manager:需要操作的顺序栈
- * @out:接收弹出的值
- * @retval : 成功返回true,失败返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- bool SeqStack_Pop(SeqStack_t *manager, DataType_t *out)
- {
- //判断顺序栈是否为空
- if ( SeqStack_IsEmpty(manager) ){
- printf("Sequence Stack is empty\n");
- return false;
- }
- //删除一个元素,栈顶元素下标-1
- *out=manager->bottom[manager->top--];
- return true;
- }
- /**
- * @name : SeqStack_print
- * @brief : 遍历顺序栈
- * @param :
- * @manager:需要操作的顺序栈
- * @retval : 成功返回true,失败返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : None
- */
- void SeqStack_print(SeqStack_t *manager)
- {
- //判断顺序栈是否为空
- if ( SeqStack_IsEmpty(manager) ){
- printf("Sequence Stack is empty\n");
- return;
- }
- //如果顺序栈不为空,则打印顺序栈的元素
- for (int i=0;i<manager->top+1;i++){
- printf("Element[%d]=%d\n",i,manager->bottom[i]);
- }
- printf("\n");
- }
- /**
- * @name : Enqueue
- * @brief : 入队
- * @param :
- * @s1:传入栈s1
- * @s2:传入栈s2
- * @Data:需要入队的数据
- * @retval : 成功返回true,失败返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : 使用栈s1和栈s2模拟队列,具有“先进先出”的思想
- */
- bool Enqueue(SeqStack_t *s1,SeqStack_t *s2,DataType_t Data)
- {
- DataType_t temp;//用于临时存储栈1出栈到栈2的元素的值
-
- //判断栈1是否已满(已满 OR 未满)
- if(s1->top+1>=maxSize){
- //判断栈2是否为空(空 OR 不空)
- if(SeqStack_IsEmpty(s2)){
- //栈2为空,一次将栈1中的元素出战到栈2
- while(!SeqStack_IsEmpty(s1)){
- if(!SeqStack_Pop(s1,&temp)) return false;
- if(!SeqStack_Push(s2,temp)) return false;
- }
- //栈1为空后,将元素插入栈1
- if(!SeqStack_Push(s1,Data)) return false;
- return true;
- }else{
- //栈2不为空,无法入队
- return false;
- }
- }else{
- // 栈1未满,直接将元素插入栈1
- if(!SeqStack_Push(s1,Data)) return false;
- }
- return true;
- }
- /**
- * @name : Dequeue
- * @brief : 出队
- * @param :
- * @s1:传入栈s1
- * @s2:传入栈s2
- * @out:接收出队的数据
- * @retval : 成功返回true,失败返回false
- * @date : 2025/7/5
- * @version : 1.0
- * @note : 使用栈s1和栈s2模拟队列,具有“先进先出”的思想
- */
- bool Dequeue(SeqStack_t *s1,SeqStack_t *s2,DataType_t *out)
- {
- DataType_t temp;//用于临时存储栈1出栈到栈2的元素的值
- //判断队列是否为空,即栈1和栈2是否都为空(空 OR 不空)
- if(SeqStack_IsEmpty(s1) && SeqStack_IsEmpty(s2)){
- return false;
- }else{
- //判断栈2是否为空(空 OR 不空)
- if(!SeqStack_IsEmpty(s2)){
- //栈2不为空,直接出栈
- if(!SeqStack_Pop(s2,out)) return false;
- }else{
- //栈2为空,将栈1内的元素一次出栈到栈2
- while(!SeqStack_IsEmpty(s1)){
- if(!SeqStack_Pop(s1,&temp)) return false;
- if(!SeqStack_Push(s2,temp)) return false;
- }
- //栈1为空后,将栈2元素出栈
- if(!SeqStack_Pop(s2,out)) return false;
- }
- }
- return true;
- }
复制代码 来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |