目录
一,队列的基本概念
队列(queue),顾名思义,就像人们排队做核酸一样,先来排队的人,先做完核酸;后来排队的人,后做完核酸。由此可得队列定义:
队列是只允许在一端进行插入操作,在另一端进行删除操作的线性表
我们称只允许插入的一头叫队尾,只允许删除的一头叫队头(进入队伍要在队尾进入,离开队伍要在队头离开)。所以,在结构体中,需要有一个指向队头的头指针(front)和一个指向队尾(rear)的尾指针,根据顺序存储的特点,还需要一个固定长度(N)的数组(data[N])来保存队列中每一个节点的数据。
进一步思考,假设队列长度N为5,我们将队列围成一个圈
为什么队列长度为5,要把圆圈分成六份呢,下面有简单解释;
队列为空时,头指针front和尾指针rear都位于0,也就是都位于同一个位置;
入队时,每插入一个数据,rear都会向后移动一位,指向插入节点的后一位,front保持不动;例如:向下标0处插入数据,rear移动到下标1处,向下标1处插入数据后,rear移动到下标2处,最终,当队列满时,rear移动到下标5处,至此不能继续插入数据,队列长度为6,只能插入5个数据。
出队时,每输出一个数据,front都会向后移动一位,rear保持不动;
队列满了之后,输出一个数据,下标0空出,所以此时继续输入数据时,rear继续移动到下标0,数据插入到下标5的位置;
当front=rear时,队列中数据全部输出,队列变为空;
计算长度和判断队列为满时,不考虑特殊情况,我们可以简单理解为队列长度=rear-front;队列为满=rear+1=front;
但是,当rear刚好等于5,或者rear小于front时,上面的公式是不成立的,所以我们可以通过取余来得出通用公式:
队列长度:(rear-front+N)%N;
队列为满:(rear+1)%N=front;
队列为空:rear=front;
二,seqqueue.h头文件
#ifndef _SEQQUEUE_H_
#define _SEQQUEUE_H_
#include <stdio.h>
#include <stdlib.h>
#define N 6
typedef int datatype;
typedef struct
{
datatype data[N];
int front;//队头下标
int rear;//队尾下标
}sequeue_t;
//1.创建空顺序队列
sequeue_t *CreateSequeue(void);
//2.入队
int IntoSequeue(sequeue_t *p,datatype data);
//3.判断是否满
int isFullSequeue(sequeue_t *p);
//4.出队
datatype OutSequeue(sequeue_t *p);
//5.判断是否空
int isEpSequeue(sequeue_t *p);
//6.计算队列长度
int LengthSequeue(sequeue_t *p);
//7.清空队列
void ClearSequeue(sequeue_t *p);
#endif
三,seqqueue.c文件
1.创建空顺序队列
空队列的创建非常简单,首先开辟结构体的内存,内存中包括一个长度为6的数组,一个front头指针和一个尾指针;
创建成功后,对front和rear赋值为0,表示当前队列为空;
最后将创建内存的首地址返回主函数中。
//1.创建空顺序队列
sequeue_t *CreateSequeue(void)
{
//堆区开辟空间,结构体指针指向空间首地址;
sequeue_t *p = (sequeue_t *)malloc(sizeof(sequeue_t));
//判断是否创建成功;
if(NULL == p)
{
printf("Create Sequeue fail\n");
}
//队首和队尾都等于下标0
p->front = 0;
p->rear = 0;
//返回开辟的堆区空间首地址
return p;
}
2.入队
入队就是按顺序插入数据,让数据一个一个在队列中排好队;
数据未出队之前,front一直处于下标0的位置,所以入队时,我们只需对rear进行操作,每次入队,都将数据插入到rear所在位置,插入后,将rear向后移动一位;
为了避免rear向后移动时,出现rear等于6(队列长度N为6)的情况,我们需要对rear后移的操作进行修改:
rear = (rear+1) % N;
//2.入队
int IntoSequeue(sequeue_t *p,datatype data)
{
//1.判断队列是否已满
if(isFullSequeue(p))
{
printf("Into Sequeue fail\n");
return -1;
}
//2.将数据插入到rear的位置
p->data[p->rear] = data;
//3.rear向后移动
p->rear = (p->rear+1) % N;
return 0;
}
3.出队
出队,就是将数据输出,并将该位视为空位,等待新数据输入;
出队的操作和入队的操作大体相同,首先将front所在位置的数据保存,然后将front向后移动,将后一位的数据作为队头,然后将出队的数据返回到主函数;
同样,为避免出队时front出现等于6的情况,我们也需要对front的后移操作进行修改:
front = (front+1) % N;
//4.出队
datatype OutSequeue(sequeue_t *p)
{
//判断队列是否为空
if(isEpSequeue(p))
{
printf("Out Sequeue fail\n");
return -1;
}
//将要出队的数据保存
datatype a = p->data[p->front];
//front向后移动一位
p->front = (p->front + 1) % N;
return a;
}
四,详细代码
#include "seqqueue.h"
//1.创建空顺序队列
sequeue_t *CreateSequeue(void)
{
//堆区开辟空间,结构体指针指向空间首地址;
sequeue_t *p = (sequeue_t *)malloc(sizeof(sequeue_t));
//判断是否创建成功;
if(NULL == p)
{
printf("Create Sequeue fail\n");
}
//队首和队尾都等于下标0
p->front = 0;
p->rear = 0;
//返回开辟的堆区空间首地址
return p;
}
//2.入队
int IntoSequeue(sequeue_t *p,datatype data)
{
//1.判断队列是否已满
if(isFullSequeue(p))
{
printf("Into Sequeue fail\n");
return -1;
}
//2.将数据插入到rear的位置
p->data[p->rear] = data;
//3.rear向后移动
p->rear = (p->rear+1) % N;
return 0;
}
//3.判断是否满
int isFullSequeue(sequeue_t *p)
{
return (p->rear+1)%N == p->front;
}
//4.出队
datatype OutSequeue(sequeue_t *p)
{
//判断队列是否为空
if(isEpSequeue(p))
{
printf("Out Sequeue fail\n");
return -1;
}
//将要出队的数据保存
datatype a = p->data[p->front];
//front向后移动一位
p->front = (p->front + 1) % N;
return a;
}
//5.判断是否空
int isEpSequeue(sequeue_t *p)
{
return p->rear == p->front;
}
//6.计算队列长度
int LengthSequeue(sequeue_t *p)
{
return (p->rear - p->front + N) % N;
}
//7.清空队列
void ClearSequeue(sequeue_t *p)
{
/*
while(!isEpSequeue(p))
{
OutSequeue(p);
}
*/
p->front = p->rear = 0;
}
如果本文中存在代码逻辑,代码完善,解释不通或不清楚的错误,还请批评指正。