引入
数据结构是计算机专业必学的一门课程,如果说算法是一个程序的操作动作,那么数据结构就是一个程序的灵魂,它让一段程序在运行的过程中,拥有足够强大的“内力”进行代码的实现。
队列其实同栈类似,也是线性表的延申,它的实质是受限制的线性表,它受到限制的部分即在原有线性表的基础上增加了两个指示标识,分别为队头指针front和队尾指针rear,并规定队头指针与队尾指针都可以随着入队、出队等操作移动。这样一个受限的线性表即被称为队列,这样的限制方式也使其拥有了特殊之处,即先进先出(FIFO)或后进后出(LILO),这样也使得队列这个特殊的数据结构有了广泛的应用。而循环队列,即将一个顺序结构的数组抽象为首尾相连的状态,使得队尾指针与队头指针能够交替出现在原队头的位置,这样也克服了顺序队列中“假上溢”的情况。“假上溢”是指:在出队的过程中,队头指针只增加不减少,使得队头指针增加之后,其之前所指的空间的地址无法被访问,造成这一部分空间无法再进行入队、出队的操作,使得分配的这部分内存空间被浪费的情况。
本篇文章将要介绍的,是循环队列(逻辑结构)的顺序(存储结构)实现。而在下文将要介绍的程序中,会通过对循环队列的初始化、判满、判空、入队、出队、遍历的分段过程,对整体代码进行分析。
第一部分 头文件与结构体的创建
创建一个结构体,因循环队列的结构与数组结构极为类似,故其结构体中的变量也与数组大体相似,即首地址,队头标识与队尾标识。其中,利用typedef,使QUEUE等价于struct SqQueue.
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct SqQueue
{
int* pBase; //队列首地址
int front; //队头标识
int rear; //队尾标识
}QUEUE;
第二部分 函数声明
提前对需操作的函数进行函数声明,对循环队列进行初始化、判满、判空、入队、出队、遍历的操作,便于为之后的具体代码的书写理清思路。
void init_queue(QUEUE* ); //初始化
bool full_queue(QUEUE*); //判满
bool empty_queue(QUEUE*); //判空
bool en_queue(QUEUE*, int); //入队
bool del_queue(QUEUE*,int* ); //出队
void traverse_queue(QUEUE*); //遍历
第三部分 主函数中的实例化与函数调用
将SqQueue结构体变量实例化为Q,调用要使用的函数,对各类操作进行验证。
int main()
{
QUEUE Q;
int val;
init(&Q);
en_queue(&Q,1);
en_queue(&Q,2);
en_queue(&Q,3);
en_queue(&Q,4);
en_queue(&Q,5);
traverse_queue(&Q);
if(del_queue(&Q,&val))
{
printf("出队成功!出队的元素为:%d\n",val);
}
else
{
printf("出队失败!\n");
}
traverse_queue(&Q);
return 0;
}
第四部分 各类操作的具体代码实现
1、循环队列的初始化 void init(QUEUE* pQ);
a. 为循环队列分配一段内存空间,将其赋给循环队列的首地址(注意:这里将循环队列的长度赋予一个确定的值6,便于代码的介绍)。
b. 判断内存是否分配成功。
c. 若分配成功,使循环队列的队头标识与队尾标识相等,并赋值为0。
void init(QUEUE* pQ)
{
pQ->pBase=(int*)malloc(sizeof(int)*6);
if(NULL==pQ->pBase)
{
printf("动态内存分配失败!\n");
exit(-1);
}
pQ->front=0;
pQ->rear=0;
}
2、判满 bool full_queue(QUEUE* pQ);
对队尾标识自增后取余,使其开始向后标识元素,若队尾标识与队头标识相等,则该循环队列已满。取余的作用:使队尾标识在标识到最后一个元素时可以从原队头的位置开始标识。
bool full_queue(QUEUE* pQ)
{
if((pQ->rear+1)%6==pQ->front)
{
return true;
}
return false;
}
3、判空 bool empty_queue(QUEUE* pQ);
当队尾标识与队头标识重合时,循环队列为空。
bool empty_queue(QUEUE* pQ)
{
if(pQ->front==pQ->rear)
{
return true;
}
else
{
return false;
}
}
4、入队 bool en_queue(QUEUE* pQ,int val);
a. 先判断循环队列是否已满。
b. 若未满,在循环队列的队尾开始插入要入队的值。
c. 使队尾标识向后移一个位置。
bool en_queue(QUEUE* pQ, int val)
{
if(full_queue(pQ))
{
return false;
}
else
{
pQ->pBase[pQ->rear]=val;
pQ->rear=(pQ->rear+1)%6;
return true;
}
}
5、出队 bool del_queue(QUEUE* pQ,int* pVal);
a. 判断循环队列是否为空。
b. 若循环队列不为空,将待出队的元素保存在临时变量中(便于在主函数具体实现中,查看已经出队的元素)。
c. 使队头标识向后移动一位。
bool del_queue(QUEUE* pQ, int* pVal)
{
if(empty_queue(pQ))
{
return false;
}
else
{
*pVal=pQ->pBase[pQ->front];
pQ->front=(pQ->front+1)%6;
return true;
}
}
6、遍历 traverse_queue(QUEUE* pQ);
a. 定义一个临时变量,使其与队头标识标识的元素相同。
b. 当临时标识未与队尾标识重合时,输出此时该临时标识标识的元素,并使其后移一位,遍历输出。
void traverse_queue(QUEUE* pQ)
{
int i=pQ->front;
while(i!=pQ->rear)
{
printf("%d ",pQ->pBase[i]);
i=(i+1)%6;
}
printf("\n");
return;
}
附上完整代码
#include<stdio.h>
#include<malloc.h>
#include<stdlib.h>
#include<stdbool.h>
typedef struct SqQueue
{
int* pBase; //队列首地址
int front; //队头标识
int rear; //队尾标识
}QUEUE;
void init_queue(QUEUE* ); //初始化
bool full_queue(QUEUE*); //判满
bool empty_queue(QUEUE*); //判空
bool en_queue(QUEUE*, int); //入队
bool del_queue(QUEUE*,int* ); //出队
void traverse_queue(QUEUE*); //遍历
int main()
{
QUEUE Q;
int val;
init(&Q);
en_queue(&Q,1);
en_queue(&Q,2);
en_queue(&Q,3);
en_queue(&Q,4);
en_queue(&Q,5);
traverse_queue(&Q);
if(del_queue(&Q,&val))
{
printf("出队成功!出队的元素为:%d\n",val);
}
else
{
printf("出队失败!\n");
}
traverse_queue(&Q);
return 0;
}
void init(QUEUE* pQ)
{
pQ->pBase=(int*)malloc(sizeof(int)*6);
if(NULL==pQ->pBase)
{
printf("动态内存分配失败!\n");
exit(-1);
}
pQ->front=0;
pQ->rear=0;
}
bool full_queue(QUEUE* pQ)
{
if((pQ->rear+1)%6==pQ->front)
{
return true;
}
return false;
}
bool empty_queue(QUEUE* pQ)
{
if(pQ->front==pQ->rear)
{
return true;
}
else
{
return false;
}
}
bool en_queue(QUEUE* pQ, int val)
{
if(full_queue(pQ))
{
return false;
}
else
{
pQ->pBase[pQ->rear]=val;
pQ->rear=(pQ->rear+1)%6;
return true;
}
}
bool del_queue(QUEUE* pQ, int* pVal)
{
if(empty_queue(pQ))
{
return false;
}
else
{
*pVal=pQ->pBase[pQ->front];
pQ->front=(pQ->front+1)%6;
return true;
}
}
void traverse_queue(QUEUE* pQ)
{
int i=pQ->front;
while(i!=pQ->rear)
{
printf("%d ",pQ->pBase[i]);
i=(i+1)%6;
}
printf("\n");
return;
}
写在最后
以上所有的操作细节与代码具体实现的思想已叙述完毕,希望对各位读者有所帮助与启发。