我们一般所说的顺序队列其实就已经是循环顺序队列了,最基础的单向队列有着太多的缺点。
队列的特点是先进先出,就像日常生活的排队一样,先去排队吃饭的人,先打到饭(单通道的情况下)。
1.顺序队列
1.1头文件
在确定了队列需要实现的功能之后,我们就可以编写头文件了,头文件中一般定义了结构体,宏定义,宏替换,例如本文中的N与data_t,N定义了队列的长度,可以只修改头文件中的内容,就可以修改每次用到的数值,data_t 则定义了队列的数据类型,当我们要储存不同的数据类型时,方便修改。
/*===============================================
* 文件名称:sequeue.h
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#ifndef __SEQUEUE__
#define __SEQUEUE__
#define N 64
typedef int data_t;
typedef struct sequeue
{
data_t buf[N];
int front;//头指针
int rear;//尾指针
}SeQueue;
SeQueue *Create_Sequeue();//创建循环队列
int Sequeue_Is_Full(SeQueue *s);//判满
int Sequeue_Is_Empty(SeQueue *s);//判空
void Enqueue(SeQueue *s,data_t data);//入队
void Dequeue(SeQueue *s,data_t *data);//出队
#endif
1.2功能函数
顺序队列的一大特点是判空与判满的条件,我们在结构体中定义了front与rear两个指针表示数组的下标,也就具备了头尾指针的作用,我们通过头尾指针的指向,来判断队列的情况
空队列:front==rear
满队列: (rear+1)%N==front
在创建队列时,front与rear都指向0,入队的话尾指针+1,出队的话头指针+1
/*===============================================
* 文件名称:sequeue.c
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sequeue.h"
SeQueue *Create_Sequeue()//创建循环队列
{
SeQueue *s=(SeQueue *)malloc(sizeof(SeQueue));
if(NULL==s)
{
printf("malloc error");
return NULL;
}
memset(s->buf,0,sizeof(s->buf));
s->front=0;
s->rear=0;
return s;
}
int Sequeue_Is_Full(SeQueue *s)//判满
{
if(s->front==(s->rear+1)%N)
return 1;
else
return 0;
}
int Sequeue_Is_Empty(SeQueue *s)//判空
{
if(s->front==s->rear)
return 1;
else
return 0;
}
void Enqueue(SeQueue *s,data_t data)//入队
{
if(Sequeue_Is_Full(s))
{
printf("队列已满,不能插入\n");
return;
}
s->buf[s->rear]=data;
s->rear=(s->rear+1)%N;
}
void Dequeue(SeQueue *s,data_t *data)//出队
{
if(Sequeue_Is_Empty(s))
{
printf("队列已空,不能删除\n");
return;
}
*data=s->buf[s->front];
s->front=(s->front+1)%N;
}
1.3主函数
主函数调用功能函数,创建了一个队列,然后入队10次,出队10次。出队是利用值传递来实现的。
/*===============================================
* 文件名称:main.c
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include "sequeue.h"
int main(int argc, char *argv[])
{
int i=0;
data_t data;
SeQueue *s=Create_Sequeue();//创建循环队列
for(i=0;i<10;i++)
{
Enqueue(s,i);//入队
printf("%2d",i);
}
puts("");
for(i=0;i<10;i++)
{
Dequeue(s,&data);//出队
printf("%2d",data);
}
puts("");
return 0;
}
运行结果
2.链式队列
2.1头文件
链式队列,我们定义了两种结构体,一种用来保存数据,称为Node节点,一种用来保存队头队尾,称为LinkSqueue,Node有多个,而LinkSequeue,只有一个
/*===============================================
* 文件名称:linkqueue.h
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#ifndef __LINKQUEUE__
#define __LINKQUEUE__
typedef int data_t;
typedef struct node
{
data_t data;
struct node *next;
}Node;
typedef struct linkqueue
{
struct node *front;
struct node *rear;
}LinkQueue;
LinkQueue *Creat_Linkqueue();//创建链式队列
int Linkqueue_Is_Empty(LinkQueue *q);//判空
void Enlinkqueue(LinkQueue *q,data_t data);//入队
void Delinkqueue(LinkQueue *q,data_t *data);//出队
#endif
2.2功能函数
功能函数中创建节点很关键,首先创建一个Node类型的头结点,用于保存后面的链表,创建一个LinkSequq节点,把内部的rear和front指向头结点。
插入函数:采用尾插法,rear指向最后一个节点,每次都创建一个新的Node节点,把数据存入Node的数据域data,然后把Node连接到rear与NULL之间,再把rear向后偏移一个,保持rear指向最后一个Node节点,保证每次插入都是最后一个。
删除函数:每次删除front节点的后一个节点。先用m把front的后一个节点的地址保存下来,再执行删除操作,删除之前可以先把m的数据域通过形参data返回,删除之后要判断队列是否为空了,如果此时队列为空(只有头结点了),则把front的地址赋给rear。
/*===============================================
* 文件名称:linkqueue.c
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include "linkqueue.h"
LinkQueue *Creat_Linkqueue()//创建链式队列
{
//创建头结点
Node *head=(Node *)malloc(sizeof(Node));
if(NULL==head)
{
printf("node malloc error");
return NULL;
}
head->data=-1;
head->next=NULL;
//给链式队列指定头尾指针的指向
LinkQueue *q=(LinkQueue *)malloc(sizeof(LinkQueue));
if(NULL==q)
{
printf("linkqueue malloc error");
return NULL;
}
//(q->front)->next=head;//不要忘了next?
//(q->rear)->next=head;
q->front=head;
q->rear=head;
return q;
}
int Linkqueue_Is_Empty(LinkQueue *q)//判空
{
if(q->front==q->rear)
{
return 1;
}
else
{
return 0;
}
}
void Enlinkqueue(LinkQueue *q,data_t data)//入队,在队尾插入
{
Node *s=(Node *)malloc(sizeof(Node));
if(NULL==s)
{
printf("node malloc error");
return ;
}
s->data=data;
s->next=q->rear->next;//s指向空
q->rear->next=s; //rear指向s
q->rear=q->rear->next; //rear指向最后一个节点
}
void Delinkqueue(LinkQueue *q,data_t *data)//出队,在队头删除,删除之后要判断,是否为空,空了要把rear指向front
{
if(Linkqueue_Is_Empty(q))//判空
{
printf("队列为空,不能删除");
return;
}
Node *m=q->front->next;//先保存要删除的第一个节点的地址
*data=m->data; //把出队的元素值返回给data
q->front->next=m->next;//删除m
free(m);
m=NULL;
if(q->front->next==NULL)//删除后队列为空了
{
q->rear=q->front;
}
}
2.3主函数
主函数简单的测试了功能函数是否能完成任务,输入了10个数,然后出队时,每次出一个,打印一次
/*===============================================
* 文件名称:main.c
* 创 建 者:
* 创建日期:2022年07月30日
* 描 述:
================================================*/
#include <stdio.h>
#include <stdlib.h>
#include "linkqueue.h"
int main(int argc, char *argv[])
{
int i=0;
data_t data;
LinkQueue *q=Creat_Linkqueue();//创建链式队列
for(i=0;i<10;i++)
{
Enlinkqueue(q,i);//入队
}
while(q->front!=q->rear)
{
Delinkqueue(q,&data);//出队
printf("%d",data);
}
puts("");
return 0;
}
运行结果: