【数据结构】C语言实现队列的相关操作

队列

队列是一种遵循先入先出规则的线性数据结构,是一种只允许在表的一端插入,在另一端删除的线性表

队尾 rear:插入端,线性表的表尾。

队头 front:删除端,线性表的表头

队列可以用数组或者链表实现

用数组实现队列时,由于队列采用头删尾插的操作方式,数组中队列的队头和队尾会不断的后移,如果到达数组末尾,那么队列就难以实现头删尾插的操作了,为了解决这种问题,采用循环队列的形式,让队头或队尾在越过数组尾部时,可以回到数组的头部然后继续操作,这种循环的形式可以通过取余操作来实现

用数组实现的队列又叫做“循环队列”

循环队列

表示

#define MAXSIZE 100
typedef struct Queue {
int data[MAXSIZE];
int front;
int rear;
}Queue;

front:记录队头的位置

rear:记录队尾后第一个空间的位置

rear 记录的位置是由入队操作决定的

当 front == rear == 0 时,假设此时有一个元素 x 入队

则代码大致为

data[rear] == x

rear++

此时 rear 的位置就是队尾后第一个空间的位置

由于采用循环的操作形式,front 和 rear 更新都是通过取余的方式

front = (front + 1) % MAXSIZE

rear = (rear + 1) % MAXSIZE

当 front == rear 时,既有可能是队空,也有可能是队满,

为了区别这两种情况,要么少用一个存储空间,要么再设一个变量 size 来记录队列的大小

假设要求队列中能存储99个数据元素,则实际上需要100(MAXSIZE)个存储空间,数组的下标范围为0到99

队满情况表(少用一个存储空间)

front rear (rear + 1) % MAXSIZE
0 99 0
1 0 1
2 1 2
3 2 3
...
98 97 98
99 98 99

于是有

队空:front == rear

队满:(rear + 1) % MAXSIZE == front

初始化

void init(Queue* Q)
{
int i = 0;
for (i = 0; i < MAXSIZE; i++)
{
Q->data[i] = 0;
}
Q->front = 0;
Q->rear = 0;
}

或者在主函数中

Queue Q = { {0},0,0 };

判断队空

int isEmpty(Queue* Q)
{
if (Q->front == Q->rear)
{
return 1;
}
else
{
return 0;
}
}

判断队满

int isFull(Queue* Q)
{
if ((Q->rear + 1) % MAXSIZE == Q->front)
{
return 1;
}
else
{
return 0;
}
}

入队

void in(Queue* Q, int x)
{
if (isFull(Q) == 1)
{
return;
}
else
{
Q->data[Q->rear] = x;
Q->rear = (Q->rear + 1) % MAXSIZE;
}
}

出队

int out(Queue* Q)
{
if (isEmpty(Q) == 1)
{
exit(0);
}
else
{
int x = Q->data[Q->front];
Q->front = (Q->front + 1) % MAXSIZE;
return x;
}
}

求队列长度

int get_length(Queue* Q)
{
int length = (Q->rear - Q->front + MAXSIZE) % MAXSIZE;
return length;
}

清空

void clear(Queue* Q)
{
Q->front = 0;
Q->rear = 0;
}

链式队列

用链表表示的队列简称为链队列

链队列带头结点,有头指针和尾指针,头指针指向头结点(不是首元结点),尾指针指向最后一个结点

队头是首元结点,队尾是尾结点,

在单链表中,用链表的头指针来表示链表,在给结构体自定义名字时,除了一个 QNode 还有一个指针 LinkList,该指针就是头指针,由于用头指针表示链表,所以名字就叫 LinkList

在链队中,同样是用头指针来表示链队,但是除了一个头指针,链队中还有一个尾指针,为了方便操作,又定义了一个结构体来存放头指针和尾指针,在对该结构体自定义名字时,名字就叫 LinkQueue

目前我的理解是,其实链表就是一个指针,没什么花里胡哨的

表示

typedef struct QueueNode
{
int data;
struct QueueNode* next;
}QueueNode;
typedef struct QueuePtr
{
QueueNode* front;
QueueNode* rear;
}LinkQueue;

当 front == rear 时,表示链队为空

初始化

void init(LinkQueue* Q)
{
Q->front = (QueueNode*)malloc(sizeof(QueueNode));
if (Q->front == NULL)
{
return;
}
Q->rear = Q->front;
Q->front->next = NULL;
}

判断队空

int isEmpty(LinkQueue* Q)
{
if (Q->front == Q->rear)
{
return 1;
}
else
{
return 0;
}
}

入队

void in(LinkQueue* Q, int x)
{
QueueNode* p = (QueueNode*)malloc(sizeof(QueueNode));
if (p == NULL)
{
return;
}
p->data = x;
p->next = NULL;
Q->rear->next = p;
Q->rear = p;
}

出队

int out(LinkQueue* Q)
{
if (isEmpty(Q) == 1)
{
exit(0);
}
QueueNode* p = Q->front->next;
int x = p->data;
if (Q->front->next == Q->rear)
{
Q->rear = Q->front;
}
Q->front->next = p->next;
free(p);
p = NULL;
return x;
}

出队时,注意当链队中除头结点外只剩一个结点时的情况,此时需要改变尾指针的指向

求队长

int get_length(LinkQueue* Q)
{
QueueNode* p = Q->front->next;
int count = 0;
while (p != NULL)
{
p = p->next;
count++;
}
return count;
}

取队头

int get_front(LinkQueue* Q)
{
if (isEmpty(Q) == 1)
{
exit(0);
}
int x = Q->front->next->data;
return x;
}

清空

遵循队列头删尾插操作原则的清空

void clear(LinkQueue* Q)
{
QueueNode* p = Q->front->next;
while (p != NULL)
{
QueueNode* pFree = p;
Q->front->next = p->next;
free(pFree);
pFree = NULL;
p = Q->front->next;
}
Q->rear = Q->front;
}

遍历清空

void clear(LinkQueue* Q)
{
QueueNode* p = Q->front->next;
while (p != NULL)
{
QueueNode* pFree = p;
p = p->next;
free(pFree);
pFree = NULL;
}
Q->front->next = NULL;
Q->rear = Q->front;
}

销毁

void destroy(LinkQueue* Q)
{
clear(Q);
free(Q->front);
Q->front = NULL;
Q->rear = NULL;
}
posted @   长白秋沙  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
点击右上角即可分享
微信分享提示