队列的实现

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

💦 队列的概念及结构

1
2
3
4
sql复制代码相比栈,队列的特性和栈是相反的。
它只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO (First In First Out) 的特性。
入队列:进行插入操作的一端称为队尾;出队列:进行删除操作的一端称为队头
对于队列来说,一种入队顺序,只有一种出队顺序

在这里插入图片描述

队列的拓展:

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

在这里插入图片描述

为了能使用Q.rear == Q.front 来区别是队空还是队满,我们常常认为出现左图时的情况即为队空的情况,此时: rear == front;而右图的情况即为队满的情况,此时:rear + 1 == front

在这里插入图片描述

关于环形队列在下面的的栈和队列面试题中会讲到

队列的应用:

1️⃣ 实际中要保证公平排队的地方可以用它

2️⃣ 广度优先遍历

💦 队列的实现

这里对于队列的实现我们使用链表的方式
在这里插入图片描述

1.初始化

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
c复制代码void QueueInit(Queue* pq)
{
assert(pq);
//把2个指针置空
pq->phead = pq->ptail = NULL;
}
2.插入

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
c复制代码void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//malloc空间,如果需要频繁的开辟空间建议再实现一个BuyQueueNode用于malloc
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
//第一次插入
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
//非第一次插入
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
}
3.判空

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
c复制代码bool QueueEmpty(Queue* pq)
{
assert(pq);
//空链表返回true,非空链表返回false
return pq->phead == NULL;
}
4.删除

函数原型
在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
c复制代码void QueuePop(Queue* pq)
{
assert(pq);
//链表为空时不能删除
assert(!QueueEmpty(pq));
//只有一个节点的情况
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个节点的情况
else
{
QueueNode* next = pq->phead->next;
free(pq->phead) ;
pq->phead = next;
}
}
5.长度

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
c复制代码QDataType QueueSize(Queue* pq)
{
assert(pq);
//如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度
int sz = 0;
QueueNode* cur = pq->phead;
while (cur)
{
sz++;
cur = cur->next;
}
return sz;
}
6.取头

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
c复制代码QDataType QueueFront(Queue* pq)
{
assert(pq);
//链表为空时不能取头
assert(!QueueEmpty(pq));

return pq->phead->data;
}
7.取尾

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
c复制代码QDataType QueueBack(Queue* pq)
{
assert(pq);
//链表为空时不能取尾
assert(!QueueEmpty(pq));

return pq->ptail->data;
}
8.销毁

函数原型

在这里插入图片描述

函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
c复制代码void QueueDestory(Queue* pq)
{
assert(pq);

QueueNode* cur = pq->phead;
//遍历链表
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
}

💦 完整代码

这里需要三个文件

1️⃣ Queue.h,用于函数的声明

2️⃣ Queue.c,用于函数的定义

3️⃣ Test.c,用于测试函数


🧿 Queue.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
c复制代码#pragma once

//头
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

//结构体
typedef int QDataType;
typedef struct QueueNode
{
struct QueueNode* next; //指向下一个节点
QDataType data; //存储整型数据
}QueueNode;

typedef struct Queue
{
QueueNode* phead;//头指针
QueueNode* ptail;//尾指针
}Queue;

//函数
void QueueInit(Queue* pq);
void QueuePush(Queue* pq, QDataType x);
bool QueueEmpty(Queue* pq);
void QueuePop(Queue* pq);
QDataType QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
void QueueDestory(Queue* pq);
🧿 Queue.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
c复制代码#include"Queue.h"

void QueueInit(Queue* pq)
{
assert(pq);
//把2个指针置空
pq->phead = pq->ptail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
assert(pq);
//malloc空间,如果需要频繁的开辟空间建议再实现一个BuyQueueNode用于malloc
QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
if (newnode == NULL)
{
printf("malloc fail\n");
exit(-1);
}
newnode->data = x;
newnode->next = NULL;
//第一次插入
if (pq->phead == NULL)
{
pq->phead = pq->ptail = newnode;
}
//非第一次插入
else
{
pq->ptail->next = newnode;
pq->ptail = newnode;
}
}
bool QueueEmpty(Queue* pq)
{
assert(pq);
//空链表返回true,非空链表返回false
return pq->phead == NULL;
}
void QueuePop(Queue* pq)
{
assert(pq);
//链表为空时不能删除
assert(!QueueEmpty(pq));
//只有一个节点的情况
if (pq->phead->next == NULL)
{
free(pq->phead);
pq->phead = pq->ptail = NULL;
}
//多个节点的情况
else
{
QueueNode* next = pq->phead->next;
free(pq->phead) ;
pq->phead = next;
}
}
QDataType QueueSize(Queue* pq)
{
assert(pq);
//如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度
int sz = 0;
QueueNode* cur = pq->phead;
while (cur)
{
sz++;
cur = cur->next;
}
return sz;
}
QDataType QueueFront(Queue* pq)
{
assert(pq);
//链表为空时不能取头
assert(!QueueEmpty(pq));

return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
assert(pq);
//链表为空时不能取尾
assert(!QueueEmpty(pq));

return pq->ptail->data;
}
void QueueDestory(Queue* pq)
{
assert(pq);

QueueNode* cur = pq->phead;
//遍历链表
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
pq->phead = pq->ptail = NULL;
}
🧿 Test.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
c复制代码#include"Queue.h"

int main()
{
//1、传二级指针
//2、返回值
//3、带哨兵位的头节点
//4、嵌套结构体 (这里使用这种方式)
QueueNode q;
//初始化
QueueInit(&q);
//插入
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
//删除
QueuePop(&q);
QueuePop(&q);
//取头
QueueFront(&q);
//取尾
QueueBack(&q);
//释放
QueueDestory(&q);

return 0;
}

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%