一、队列的概念及结构
1.队列的概念
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出的性质。 FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾
出队列:进行删除操作的一端称为队头。
2.队列的结构
二、队列的实现
队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
实现功能
QueueInit 队列初始化
QueueEmpty 判断队列是否为空
QueueDestroy 队列销毁
QueuePush 入队
QueuePop 出队
QueueFront 返回队列的队头元素QueueBack 返回队列的队尾元素
QueueSize 返回队列的大小
Queue.h
#pragma once
#define _CRT_NO_SECURE_WARNINGS
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>
typedef int DataType;
typedef struct QueueNode
{
struct QueueNode* next;
DataType val;
}QueueNode;
typedef struct Queue
{
struct QueueNode* head;
struct QueueNode* tail;
}Queue;
extern void QueueInit(Queue* p);
extern void QueueDestroy(Queue* p);
extern void QueuePush(Queue* p, DataType x);
extern bool QueueEmpty(Queue* p);
extern int QueueSize(Queue* p);
extern void QueuePop(Queue* p);
extern DataType QueueFront(Queue* p);
extern DataType QueueBack(Queue* p);
Queue.c
#include"queue.h"
void QueueInit(Queue* p)
{
assert(p);
p->head = NULL;
p->tail = NULL;
}
void QueueDestroy(Queue* p)
{
assert(p);
QueueNode* cur = p->head;
while (cur)
{
QueueNode* next = cur->next;
free(cur);
cur = next;
}
p->head = p->tail = NULL;
}
void QueuePush(Queue* p, DataType x)
{
assert(p);
QueueNode* new_node = (QueueNode*)malloc(sizeof(QueueNode));
if (!new_node)
{
perror("malloc");
exit(-1);
}
new_node->val = x;
new_node->next = NULL;
if (QueueEmpty(p))
p->head = p->tail = new_node;
else
{
p->tail->next = new_node;
p->tail = p->tail->next;
}
}
bool QueueEmpty(Queue* p)
{
assert(p);
return (p->head == NULL) ? true : false;
}
int QueueSize(Queue* p)
{
if (QueueEmpty(p)) return 0;
int count = 1;
QueueNode* tail = p->head;
while (tail != p->tail)
{
tail = tail->next;
count++;
}
return count;
}
void QueuePop(Queue* p)
{
assert(p);
if (QueueEmpty(p))
{
printf("The queue is empty!\n");
return;
}
if (QueueSize(p) == 1)
{
free(p->head);
p->head = p->tail = NULL;
}
else
{
QueueNode* next = p->head->next;
free(p->head);
p->head = next;
}
}
DataType QueueFront(Queue* p)
{
assert(p);
if (QueueEmpty(p))
{
printf("The queue is empty!\n");
return 0;
}
return p->head->val;
}
DataType QueueBack(Queue* p)
{
assert(p);
if (QueueEmpty(p))
{
printf("The queue is empty!\n");
return 0;
}
return p->tail->val;
}
test.c
#include"queue.h"
void test1()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueueDestroy(&q);
}
void test2()
{
Queue q;
QueueInit(&q);
QueuePush(&q, 1);
QueuePush(&q, 2);
QueuePush(&q, 3);
QueuePush(&q, 4);
int val;
val = QueueFront(&q);
printf("%d\n", val);
val = QueueBack(&q);
printf("%d\n", val);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
QueuePop(&q);
val = QueueFront(&q);
printf("%d\n", val);
val = QueueBack(&q);
printf("%d\n", val);
}
int main()
{
test1();
printf("------------------------------------------------\n");
test2();
return 0;
}
三、循环队列
通常我们所使用的大都是循环队列,如操作系统课程讲解生产者消费者模型时可以就会使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。但由于循环的原因,我们就不能当头尾指针重叠在一起来判断队列为空还是满的情况,为了避免这种情况,我们又引入了边界条件的概念。
重点:循环队列,无论使用数组实现还是链表实现,都要多开一个空间,也就意味着,要是一个存k个数据的循环队列,要开k+1个空间否则无法实现判空和判满。