二、队列(链式、数组)

说明

队列是最常见基础的一种数据结构,有着先入先出的特点,在算法的实现上,经常会使用到队列。本文主要目标是实现一个可以存储多种数据类型的队列,并提供常用的队列方法。本文采用三种方式来创建队列:定长数组、创建时指定长度数组、链式队列,提供的队列方法:

  • 创建队列
  • 队列判空
  • 队满判断
  • 入队列
  • 出队列
  • 获取队首元素
  • 销毁队列

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

  • 24
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值