一、定义
- 队列(queue)是限定在表的一端进行插入,表的另一端进行删除的数据结构。先进先出。
二、队列设计
//结点定义
typedef struct node{
int data;
struct node *next;
}node;
//队列定义,队首指针和队尾指针
typedef struct queue{
node *front; //头指针
node *rear; //尾指针
}queue;
三、初始化
初始化将头尾两个结点指向空,表示是一个空队列
//初始化结点
node *init_node(){
node *n=(node*)malloc(sizeof(node));
if(n==NULL){ //建立失败,退出
exit(0);
}
return n;
}
//初始化队列
queue *init_queue(){
queue *q=(queue*)malloc(sizeof(queue));
if(q==NULL){ //建立失败,退出
exit(0);
}
//头尾结点均赋值NULL
q->front=NULL;
q->rear=NULL;
return q;
}
四、基本操作
- 判空: (直接就是判断队列头指针是否是空值即可)
q->front==NULL; - 入队
需要特判一下队列是否为空。
如果队列为空的话,需要将头指针和尾指针一同指向第一个结点,即front=n;rear=n。
不为空的时候,我们只需要将尾结点向后移动,通过不断移动next指针指向新的结点构成队列即可。
//入队操作
void push(queue *q,int data){
node *n =init_node();
n->data=data;
n->next=NULL; //采用尾插入法
//if(q->rear==NULL){ //使用此方法也可以
if(empty(q)){
q->front=n;
q->rear=n;
}else{
q->rear->next=n; //n成为当前尾结点的下一结点
q->rear=n; //让尾指针指向n
}
}
- 出队
队列为空,直接结束;
在队列不为空的情况下, 将队列的头指针指向头指针当前指向的下一个元素并释放掉当前元素;
队列只有一个元素了(即头尾指针均指向了同一个结点),直接将头尾两指针制空(NULL)并释放这一个结点。
void pop(queue *q){
node *n=q->front;
if(empty(q)){
return ; //此时队列为空,直接返回函数结束
}
if(q->front==q->rear){
q->front=NULL; //只有一个元素时直接将两端指向制空即可
q->rear=NULL;
free(n); //记得归还内存空间
}else{
q->front=q->front->next;
free(n);
}
}
- 遍历
队列不为空的情况下,通过结点的next指向依次遍历并输出元素
printf("%d\t",n->data);
n=n->next;
五、循环队列
"假溢出": 队列用的存储区还没有满,但队列却发生了溢出。
(出队操作头指针后移,入队尾指针后移,头部存储空置)
优化:给定队列的大小范围,在原有队列的基础上,只要队列的后方满了,就从这个队列的前面开始进行插入,以达到重复利用空间的效果。
由于循环队列的设计思维更像一个环,因此常使用一个环图来表示,但注意其不是一个真正的环,循环队列依旧是单线性的。
- 数据结构设计
#define maxsize 10 //表示循环队列的最大容量
//循环队列的结构设计
typedef struct cir_queue{
int data[maxsize];
int rear;
int front;
}cir_queue;
- 初始化
//初始化
cir_queue *init(){
cir_queue *q = (cir_queue*)malloc(sizeof(cir_queue));
if(q==NULL){
exit(0); //申请内存失败,退出程序
}
q->front=0;
q->rear=0;
return q;
}
- 入队
这里推荐使用余数法,即无论如何求余都是在这片空间内进行操作。
新入队位置:(q->rear+1)%maxsize
队列已满的判断:当我们rear指针的下一个位置就是front的位置的时候,即已满。
#define maxsize 10 //表示循环队列的最大容量
//入队操作push
void push(cir_queue *q,int data){
if((q->rear+1)%maxsize==q->front){
printf("溢出,无法入队\n");
return;
}else{
q->rear=(q->rear+1)%maxsize;
q->data[q->rear]=data;
}
}
- 出队
将front指针后移一位
//出队操作pop
void pop(cir_queue *q){
if(q->rear==q->front){
printf("队列为空,无法出队\n");
return;
}else{
q->data[q->front]=0;
q->front=(q->front+1)%maxsize;
}
}
- 遍历
借助一个临时变量储存位置front的位置信息,利用i逐步向后移动,直到i到达了rear的位置,遍历结束。
//遍历队列
void print(cir_queue *q){
int i=q->front;
while(i!=q->rear){
i=(i+1)%maxsize;
printf("%d\t",q->data[i]);
}
}