(C语言)手撕数据结构之——队列

Posted waywt1

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(C语言)手撕数据结构之——队列相关的知识,希望对你有一定的参考价值。

目录

队列的概念及结构

队列的实现方式

数组队列

链式队列

链式队列的实现

初始化队列

入队列

出队列

获取队列头部元素

获取队列尾部元素

获取队列中有效元素个数

检测队列是否为空

销毁队列

实现队列的全部代码

测试用例


队列的概念及结构

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)。

入队列:进行插入操作的一端称为队尾。

出队列:进行删除操作的一端称为队头。

队列的实现方式

实现队列有两种方式:1.用数组实现,2.用链表实现。

数组队列

缺点:元素出队列时需要将后面的数据向前面挪,效率比较低。

链式队列

在队列中保存指向队头队尾的两个指针,出队时只需将指向队头的指针指向原队头的下一个,入队时直接在队尾的后面入即可

故使用链表的结构实现会更优一点。

链式队列的实现

首先创建该队列中单个元素的结构体,然后创建一个队列结构体,包含指向头、尾两个指针。

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

下面给出需要实现的接口:

//初始化队列
void QueueInit(Queue* pq);

//入队列
void QueuePush(Queue* pq, QDataType x);
//出队列
void QueuePop(Queue* pq);

//获取队列头部元素
QDataType QueueFront(Queue* pq);
//获取队列队尾元素
QDataType QueueBack(Queue* pq);

//获取队列中有效元素个数
int QueueSize(Queue* pq);

//检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDestroy(Queue* pq);

初始化队列

队列初始化只需将指向头、尾的两个指针指向空即可。

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
}

入队列

首先申请空间创建该元素,如果队列中无元素,则将该元素作为队列的头和尾;如果队列中有元素,则直接将该元素连接到队列的尾处,并将队列的尾指针指向该元素。

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		printf("malloc fail\\n");
		exit(-1);
	}

	newNode->data = x;
	newNode->next = NULL;

	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

出队列

出队列时先检测队列中是否有元素,若队列中无元素,则应让程序直接报错。

有元素时分为一个或多个元素:

一个元素:直接释放该节点,并将原队列中的头、尾指针指向空。

多个元素:先保存头指针指向节点的下一个节点,然后释放头节点,并将头指针指向原保存值。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);//若队列为空,无元素可出队列,直接报错

	//1.一个元素
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
    //2.多个元素
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

获取队列头部元素

获取元素时先检测队列是否有元素,若无元素则直接报错,有元素则返回头指针指向节点的数据。

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

获取队列尾部元素

与获取队头元素相类似。

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail);

	return pq->tail->data;
}

获取队列中有效元素个数

从队头开始遍历整个队列并计数即可,遍历时应创建一个新的变量进行遍历,保证不改变头指针。

int QueueSize(Queue* pq)
{
	assert(pq);

	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

检测队列是否为空

如果为空返回非零结果,如果非空返回0

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->head == NULL;
}

销毁队列

队列中每个元素都为新开辟的节点,故需要逐个销毁。将所有节点销毁完后将头、尾指针指向空。

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

实现队列的全部代码

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

typedef int QDataType;

typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QNode;

typedef struct Queue
{
	QNode* head;
	QNode* tail;
}Queue;

//初始化队列
void QueueInit(Queue* pq);

//队尾入
void QueuePush(Queue* pq, QDataType x);
//队头出
void QueuePop(Queue* pq);

//获取队列头部元素
QDataType QueueFront(Queue* pq);
//获取队列队尾元素
QDataType QueueBack(Queue* pq);

//获取队列中有效元素个数
int QueueSize(Queue* pq);

//检测队列是否为空,如果为空返回非零结果,如果非空返回0
bool QueueEmpty(Queue* pq);

//销毁队列
void QueueDestroy(Queue* pq);

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->head = pq->tail = NULL;
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		printf("malloc fail\\n");
		exit(-1);
	}

	newNode->data = x;
	newNode->next = NULL;

	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	//1.一个
	//2.多个
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QNode* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);

	return pq->head->data;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->tail);

	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);

	int size = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->head == NULL;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}

	pq->head = pq->tail = NULL;
}

测试用例

void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
    printf("%d ", QueueBack(&q));

	QueuePop(&q);
	printf("%d ", QueueFront(&q));
	QueuePop(&q);
	printf("%d ", QueueFront(&q));

	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

    printf("QueueSize:%d\\n", QueueSize(&q));

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\\n");

	QueueDestroy(&q);
}

int main()
{
    TestQueue();
    return 0;
}

以上是关于(C语言)手撕数据结构之——队列的主要内容,如果未能解决你的问题,请参考以下文章

「Linux」400行纯C语言代码带你「手撕线程池」

手撕STL——队列的内部实现

手撕C语言标准库qsort(自我实现简化高效版C风格泛型快排)

手把手写C++服务器(35):手撕代码——高并发高QPS技术基石之非阻塞send万字长文

黑马程序员 C语言数据结构与算法之线性表(链表/栈/队列/顺序表)

队列(Queue)之顺序存储结构的相关实现(C语言)