1.队列的概念及结构
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有"先进先出"FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾 出队列:进行删除操作的一端称为队头
2.队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低,难度系数大。
3.队列接口的定义
#include<stdbool.h>
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int INT;
typedef struct QueueNode
{
//队中数值
INT val;
//下位指针
struct QueueNode*next;
}ST1;
typedef struct Queue
{ //队头
ST1*p1;
//队尾
ST1*p2;
//队数值
int size;
}ST2;
//初始化队列
void QueueInit(ST2*pst);
//销毁队列
void Queuestroy(ST2*pst);
//队尾插入队列
void QueuePush(ST2*pst,INT x);
//队头出队列
void QueuePop(ST2*pst);
//获取队头元素
INT QueueFront(ST2*pst);
//获取队尾元素
INT QueueBeack(ST2*pst);
//判断队列是否为空
int QueueEmpty(ST2*pst);
//队列中有效位数
int QueueSize(ST2*pst);
4.代码组成与实现
4.1队列初始化
void QueueInit(ST2*pst)
{
assert(pst);
pst->p1=pst->p2=NULL;
pst->size=0;
}
4.2.队尾插入队列
队列的规则是先进先出,数据是从后向前插入的,也就相当于链表的尾插。
void QueuePush(ST2*pst,INT x)
{
assert(pst);
//开辟空间
ST1*ret=(ST1*)malloc(sizeof(ST1));
if(ret==NULL)
{
perror(" ");
return ;
}
//赋初值
ret->next=NULL;
ret->val=x;
//如果是第一次,则直接插入
if(pst->p2==NULL)
{
pst->p1=pst->p2=ret;
}
//否则使篇的下一个节点指向他,并走向下一个节点
else{
pst->p2->next=ret;
pst->p2=ret;
}
pst->size++;
}
4.3.队头出队列
队列的规则是先进先出,出数据就是将队头移向下一位,所以只需要将队头下移,有效位减一位即可。
当然不乏出现特殊情况,列如只有一个元素时,就会出现野指针的情况,所以p2要给NULL。
void QueuePop(ST2*pst)
{
assert(pst);
assert(pst->p1);
//保持p1节点
ST1*del=pst->p1;
//使p1走向p1的下一项
pst->p1=pst->p1->next;
//从而释放掉p1先节点
free(del);
del=NULL;
//如果p1是最后一个节点或者NULL则尾节点p2指向NULL
if(pst->p1==NULL)
{
pst->p2=NULL;
}
pst->size--;
}
4.4.获取队头元素
相对来说没有什么难点,只需要注意是否有值即可。
INT QueueFront(ST2*pst)
{
assert(pst);
assert(pst->p1);
//返回p1节点的值
return pst->p1->val;
}
4.5.获取队尾元素
INT QueueBeack(ST2*pst)
{
assert(pst);
assert(pst->p2);
//返回p2节点的值
return pst->p2->val;
}
4.6.判断队列是否为空
bool QueueEmpty(ST2*pst)
{
assert(pst);
return pst->p1==NULL;
}
4.7.队列中有效位数
int QueueSize(ST2*pst)
{
assert(pst);
return pst->size;
}
4.8.销毁队列
队列是由动态开辟的,释放空间需要逐条释放。
void Queuestroy(ST2*pst)
{
assert(pst);
ST1 *cur=pst->p1;
while(cur)
{
ST1*next=cur->next;
free(cur);
cur=next;
}
pst->p1=pst->p2=NULL;
pst->size=0;
}
5.扩展
另外扩展了解一下,实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。
代码的基本组成分为:初始化队列,插入数据,删除数据,判断是否满,判断是否空,输出头元素,输出尾元素,释放队列。
5.1队列定义
typedef struct {
//存放数据的数值(队列)
int *a;
//队头
int front;
//队尾
int rear;
//队列大小
int n;
} MyCircularQueue;
5.2.初始化队列
由于是循环队列无法判断队尾的位置所以多开一个空间来判断队尾。
MyCircularQueue* myCircularQueueCreate(int k)
{
MyCircularQueue* obj(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
int * tmp=(int *)malloc(sizeof(int)*(k+1));
obj->a=tmp;
obj->front=0;
obj->rear=0;
obj->n=k;
return obj;
}
5.3.判断队是否被装满
bool myCircularQueueIsFull(MyCircularQueue* obj) {
if((obj->rear+1)%(obj->n+1)==obj->front)
{
return true;
}
return false;
}
5.4.插入数据
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
if(myCircularQueueIsFull( obj))
{
return false;
}
obj->a[obj->rear]=value;
obj->rear++;
//实现数组循环
obj->rear=obj->rear%(obj->n+1);
return true;
}
5.5.判断是否空
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
if(obj->front==obj->rear)
{
return true;
}
return false;
}
5.6删除数据
bool myCircularQueueDeQueue(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty( obj))
{
return false;
}
//有效位是front到rear之间的数
obj->front++;
obj->front=obj->front%(obj->n+1);
return true;
}
5.7输出头元素
int myCircularQueueFront(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty( obj))
{
return -1;
}
return obj->a[obj->front];
}
5.8输出尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
if(myCircularQueueIsEmpty( obj))
{
return -1;
}
//if(obj->rear==0)
//return obj->a[k];
//return obj->a[obj->rear-1];
return obj->a[(obj->rear-1+obj->n+1)%(obj->n+1)];
}
5.9释放队列
void myCircularQueueFree(MyCircularQueue* obj) {
free(obj->a);
free(obj);
}
如果喜欢麻烦免费的小星星点一下。