说明
队列是最常见基础的一种数据结构,有着先入先出的特点,在算法的实现上,经常会使用到队列。本文主要目标是实现一个可以存储多种数据类型的队列,并提供常用的队列方法。本文采用三种方式来创建队列:定长数组、创建时指定长度数组、链式队列,提供的队列方法:
- 创建队列
- 队列判空
- 队满判断
- 入队列
- 出队列
- 获取队首元素
- 销毁队列
1、定长数组实现队列
1.1、队列结构定义
队列的长度在创建时就已经确定,且无法更改。使用void*
指定数组中的内容皆为指针形式,这样无论什么类型数据存入都能支持。结构中指定了队列的最大容量size
、当前长度length
、队首指针front
、队尾指针rear
。
#define QUEUE_FIX_LEN 50 //固定长度
//队列节点(固定长度)
typedef struct ArrayQueueFix{
void* datas[QUEUE_FIX_LEN]; //数据内容,长度固定
int size; //最大容量
int length; //当前长度
int front; //队首
int rear; //队尾
}ArrayQueueFix;
1.2、队列创建
分配队列对象的空间,以及初始哈队列对象的属性。
//创建队列,队列长度固定且默认,由QUEUE_FIX_LEN决定
ArrayQueueFix* createArrayQueueFix(){
ArrayQueueFix* queue = (ArrayQueueFix*) malloc(sizeof(ArrayQueueFix));
queue->size = QUEUE_FIX_LEN;
queue->length=0;
queue->front = 0;
queue->rear = 0;
return queue;
}
1.3、队列判空
//队列是否已空
int fixQueueIsEmpty(ArrayQueueFix *queue){
if(queue->length == 0){
return 1;
}else{
return 0;
}
}
1.4、队满判断
//队列是否已经满了
int fixQueueIsFull(ArrayQueueFix *queue){
if(queue->length == queue->size){
return 1;
}else{
return 0;
}
}
1.5、入队列
先判断队列是否已满,若队列已满,则入队列失败;否则,将新元素放入队尾指针位置,队尾指针+1;再将队尾指针与队列长度取余,得到队尾指针的真正位置(此时队列为循环队列)。
//入队列
void fixQueueEnQueue(void* data,ArrayQueueFix *queue){
if(fixQueueIsFull(queue) == 1){
printf("入队列失败,队列已满!\n");
}else{
queue->datas[queue->rear] = data;
queue->length = queue->length+1;
queue->rear = queue->rear+1;
queue->rear = queue->rear % queue->size;
}
}
1.6、出队列
先判断队列是否为空,如果为空,则出队列失败;否则,根据队首指针,取出队首元素。然后队首指针+1;再将队首指针与队列长度取余,得到队首指针的真正位置(此时队列为循环队列)。
//出队列
void* fixQueueDeQueue(ArrayQueueFix *queue){
if(fixQueueIsEmpty(queue) == 1){
printf("出队列失败,队列已空!\n");
return NULL;
}else{
void* data = queue->datas[queue->front];
queue->length = queue->length - 1;
queue->front = queue->front + 1;
queue->front = queue->front % queue->size;
return data;
}
}
1.7、获取队首元素
若队列不为空,则根据队首指针,取出队首元素,队首指针不做变化。
//获取队首元素
void* fixQueueGetQueue(ArrayQueueFix *queue){
if(fixQueueIsEmpty(queue) == 1){
printf("出队列失败,队列已空!\n");
return NULL;
}else{
void* data = queue->datas[queue->front];
return data;
}
}
1.8、队列销毁
void fixQueueDestroy(ArrayQueueFix *queue){
free(queue);
}
2、指定长度数组队列
2.1、队列结构定义
队列长度不可变,但队列长度由创建时给定。这里的void**
并非二维数组,而是一个void*
式的数组。
//队列节点(固定长度)
typedef struct ArrayQueueDynamic{
void** datas; //数据内容,长度动态
int size; //最大容量
int length; //当前长度
int front; //队首
int rear; //队尾
}ArrayQueueDynamic;
2.2、队列创建
队列创建时需要分配队列对象自己的空间,还要分配对象中数据所在数组的空间,并初始化基本信息。
//创建队列,队列长度固定且默认,由size决定
ArrayQueueDynamic* createArrayQueueDynamic(int size){
ArrayQueueDynamic* queue = (ArrayQueueDynamic*) malloc(sizeof(ArrayQueueDynamic));
queue->size = size;
queue->length=0;
queue->front = 0;
queue->rear = 0;
queue->datas = (void**)malloc(sizeof(void*) * size);
return queue;
}
2.3、队列判空
等价于定长数组的队列判空
2.4、队满判断
等价于定长数组的队满判断
2.5、入队列
等价于定长数组的入队列
2.6、出队列
等价于定长数组的出队列
2.7、获取队首元素
等价于定长数组的获取队首元素
2.8、销毁队列
数据元素所在数组也需要手动释放
void dynamicQueueDestroy(ArrayQueueDynamic *queue){
free(queue->datas);
free(queue);
}
3、链式队列
3.1、结构定义
链式队列,仍然保持队列的特性:先进先出。但采用链表来实现队列,不需要考虑链表的容量问题。这里需要定义链表的结点结构以及队列对象的结构,本例采用双向链表实现。
//队列节点
typedef struct QueueNode{
void* data;
struct QueueNode *next;//后继元素
struct QueueNode *front;//前驱元素
}QueueNode;
//队列对象(链式队列)
typedef struct LinkedQueue{
QueueNode *head; //队列头
QueueNode *rear; //队列尾
int size; //最大容量
}LinkedQueue;
3.2、创建队列
先分配队列的空间,再创建一个空的链表节点,作为链表的头结点,该结点不参与队列的数据存储。设置好队首和队尾指针
//创建队列,队列长度固定且默认,由size决定
LinkedQueue* createQueueLink(){
LinkedQueue* queue = (LinkedQueue*) malloc(sizeof(LinkedQueue));
queue->size = 0;
queue->head = (QueueNode*)malloc(sizeof(QueueNode));
queue->rear = queue->head;
queue->rear->next = NULL;
queue->rear->front = NULL;
return queue;
}
3.3、队列判空
//队列是否已空
int linkedQueueIsEmpty(LinkedQueue *queue){
if(queue->size == 0){
return 1;
}else{
return 0;
}
}
3.4、队满判断
链式队列不会满
3.5、入队列
入队列元素分配 链表结点空间,移动指针,将元素放入队尾
//入队列
void linkedQueueEnQueue(void* val,LinkedQueue *queue){
QueueNode *node = (QueueNode*)malloc(sizeof(QueueNode));
node->data = val;
node->front = queue->rear; //新元素的前驱为 队列的尾元素
node->next = NULL; //新元素的后继为 NULL
queue->rear->next = node; //尾元素的后继为 新元素
queue->rear = node; //移动队尾指针到新元素
queue->size = queue->size+1;
}
3.6、出队列
如果队列不为空,则取出队首元素,移动指针保证链表的完整,并释放队首元素所在链表结点空间,然后返回数据元素。
//出队列
void* linkedQueueDeQueue(LinkedQueue *queue){
if(linkedQueueIsEmpty(queue) == 1){
printf("出队列失败,队列已空!\n");
return NULL;
}else{
QueueNode *node = queue->head;
queue->head = queue->head->next;
queue->size = queue->size - 1;
void* val = queue->head->data;
free(node);
return val;
}
}
3.7、获取队首元素
如果队列不为空,则取出队首元素,返回数据内容即可,不需要移动指针和释放节点。
//获取队首元素
void* linkedQueueGetQueue(LinkedQueue *queue){
if(linkedQueueIsEmpty(queue) == 1){
printf("出队列失败,队列已空!\n");
return NULL;
}else{
QueueNode *node = queue->head->next;
void* val = node->data;
return val;
}
}
3.8、销毁队列
队列中元素可能不为空,要先释放队列中元素的空间,即链表的空间,最后再释放队列的空间。
void linkedQueueDestroy(LinkedQueue *queue){
QueueNode *node = queue->head;
while(node != NULL){
free(node);
node = node->next;
}
free(queue);
}
源码地址:https://download.csdn.net/download/Waiting_Love/89583883