数据结构_队列(基于顺序表和链表)
队列是只允许在一端进行数据操作,在另一端进行删除数据的一种特殊线性表,进行插入操作(入队列)一端的称为队尾,进行删除操作(出队列)的一端称为队头,队列具有先进先出的特性。队列和栈相似,基于顺序表或链表实现,限制对顺序表、链表的操作,减少出错的可能性。
队列按用途可分为三类:
- 普通队列
- 消息队列
-
优先级队列
基于顺序表、链表实现队列的基本操作:
队列的基本操作:入队列、出队列、取队首元素,如下所示:
一、顺序表实现队列
用定长数组实现队列,要对队首和队尾操作,需要搬运数组元素,效率较低,可以记住队首、队尾的位置。进行操作入队列操作时,修改队尾的位置,队尾到达数组上限时,先确定数组是否还有空间,有的话移动到数组头部;出队列修改队首的位置,如果队首到达数组上限,让队首回到数组头部。
以下为代码实现:
// 头文件及结构体声明
1 #pragma once
2
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<string.h>
6 #include<stddef.h>
7
8 typedef char SeqListQueueType;
9
10 #define SeqListQueueMaxSize 1000
11
12 typedef struct SeqListQueue{
13 SeqListQueueType data[SeqListQueueMaxSize];
14 size_t head;
15 size_t tail;
16 size_t size;
17 }SeqListQueue;
1.队列初始化、销毁
// 初始化
3 void SeqListQueueInit(SeqListQueue* queue)
4 {
5 if(queue == NULL){
6 return;
7 }
8 queue->size = 0;
9 queue->head = 0;
10 queue->tail = 0;
11 return;
12 }
// 销毁
14 void SeqListQueueDestory(SeqListQueue* queue)
15 {
16 if(queue == NULL){
17 return;
18 }
19 queue->size = 0;
20 queue->head = 0;
21 queue->tail = 0;
22 queue = NULL;
23 return;
24 }
2.入队列、出队列
// 入队列
26 void SeqListQueuePush(SeqListQueue* queue, SeqListQueueType value)
27 {
28 if(queue == NULL){
29 return;
30 }
31 if(queue->size >= SeqListQueueMaxSize){
32 // 队列已满
33 return;
34 }
35 if(queue->tail > SeqListQueueMaxSize){ // 修改 tail
36 queue->tail = 0;
37 }
38 // 元素入队列,tail 值+1,如果 tail 到达数组尾部,让 tail 回到数组的头部
39 queue->data[queue->tail ++] = value; // 入队列
40 queue->size ++;
41 return;
42 }
// 出队列
44 void SeqListQueuePop(SeqListQueue* queue)
45 {
46 if(queue == NULL){
47 return;
48 }
49 if(queue->size == 0){
50 // 空队列
51 printf("空队列.\n");
52 return;
53 }
54 if(queue->head > SeqListQueueMaxSize){
55 queue->head = 0;
56 }
57 // 元素出队列,head +1 ,如果 head 到数组尾部,让 head 回到数组的头部
58 queue->head ++;
59 queue->size --;
60 return;
61 }
3.取队首元素
63 int SeqListQueueFront(SeqListQueue* queue, SeqListQueueType* front)
64 {
65 if(queue == NULL || front == NULL){
66 return 0;
67 }
68 if(queue->size == 0){ // 空队列
69 return 0;
70 }
71 *front = queue->data[queue->head];
72 return 1;
73 }
二、链表实现队列
用带有头结点的单向链表实现。进行入队列操作时,采用尾插,遍历链表;出队列时,采用头删。以下为代码实现:
// 头文件及结构体声明
1 #pragma once
2
3 #include<stdio.h>
4 #include<stdlib.h>
5 #include<string.h>
6 #include<stddef.h>
7
8 typedef char LinkQueueType;
9
10 typedef struct LinkQueue{
11 LinkQueueType data;
12 struct LinkQueue* next;
13 }LinkQueue;
1.队列初始化、销毁
// 初始化
3 void LinkQueueInit(LinkQueue** queue)
4 {
5 if(queue == NULL){
6 return;
7 }
8 *queue = (LinkQueue*)malloc(sizeof(LinkQueue));
9 (*queue)->data = 0;
10 (*queue)->next = NULL;
11 return;
12 }
// 销毁
14 void LinkQueueDestroy(LinkQueue** queue)
15 {
16 if(queue == NULL){
17 return;
18 }
19 free(*queue);
20 *queue = NULL;
21 return;
22 }
2.入队列、出队列
// 创建结点
24 LinkQueue* LinkQueueCreateNode(LinkQueueType value)
25 {
26 LinkQueue* new_node = (LinkQueue*)malloc(sizeof(LinkQueue));
27 new_node->data = value;
28 new_node->next = NULL;
29 return new_node;
30 }
// 销毁结点
32 void LinkQueueDestroyNode(LinkQueue* pos)
33 {
34 if(pos){
35 free(pos);
36 return;
37 }
38 }
// 入队列
40 void LinkQueuePush(LinkQueue* queue, LinkQueueType value)
41 {
42 if(queue == NULL){
43 return;
44 }
45 LinkQueue* new_node = LinkQueueCreateNode(value);
46 if(queue->next == NULL){
47 queue->next = new_node;
48 return;
49 }
50 LinkQueue*cur = queue->next;
51 while(cur->next != NULL){
52 cur = cur->next;
53 }
54 cur->next = new_node;
55 return;
56 }
// 出队列
58 void LinkQueuePop(LinkQueue* queue)
59 {
60 if(queue == NULL){
61 return;
62 }
63 if(queue->next == NULL){
64 return;
65 }
66 LinkQueue* tmp = queue->next;
67 queue->next = tmp->next;
68 LinkQueueDestroyNode(tmp);
69 return;
70 }
3.取队首元素
72 int LinkQueueFront(LinkQueue* queue, LinkQueueType* front)
73 {
74 if(queue == NULL || front == NULL){
75 return 0;
76 }
77 if(queue->next == NULL){
78 return 0;
79 }
80 *front = queue->next->data;
81 return 1;
82 }
三、顺序表、链表实现队列差异
顺序表:定长,对队列进行入队列操作的时候,需要判断队列是否已满,保存队首、队尾的下标,简化了队列操作,时间复杂度为 O(1)。
链表:动态申请内存,对队列进行入队列操作时,需要遍历链表找到队尾元素进行入队列,时间复杂度为 O(n)。