【数据结构与算法从0到1 - C实现】五、队列的解析与实现

上一节:【数据结构与算法从0到1 - C实现】四、栈的解析与实现

下一节:待续……


目录

1 队列的概念及结构

2 队列的C实现

3 练习题

4 结语


1 队列的概念及结构

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

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

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

 数据结构中的队列,很容易会让我们想到生活中的队列,比如饭堂排队打饭的场景。新来的打饭的人相当于入队列的数据,位于队尾;刚打完饭要离开的人相当于出队列的数据,位于队头。

2 队列的C实现

队列的实现通过数组(顺序表)和链表都可以实现,但使用链表的结构实现更好一些。

因为对于数组来说,不论是前端作为队头,还是后端作为队头,队头和队尾必定会有一个需要让整个数组移动的情况,这样就会造成比较大的消耗。

而链表就没有这样的烦恼,可以直接在尾端(或头端)删掉(或增加)数据,非常方便。但对于一般单链表还有个缺陷,在每次尾端增加数据(入队列)时,都要从头进行遍历链表,以获得链表末端地址。基于这个原因,我们可以对单链表进行改造,增加一个尾节点,专门定位链表尾部地址。那么我们在构建队列的时候,可以使用一个结构体,该结构体中包含链表的头结点尾节点,头结点主要负责数据出队列,尾节点主要负责数据入队列。

 创建头文件Queue.h,完成链表式队列的节点的定义,队列结构体的定义,以及各个队列接口函数的声明;这些接口函数有:队列的初始化、销毁、入队列、出队列、返回头部数据、返回尾部数据、返回队列的大小、判断队列是否为空。

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.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 QueueDestroy(Queue* pq);

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

// 出队列
void QueuePop(Queue* pq);

// 取队头数据
QDataType QueueFront(Queue* pq);

// 取队尾数据
QDataType QueueBack(Queue* pq);

// 计算队列中有多少个数据
int QueueSize(Queue* pq);

// 判断队列是否为空
bool QueueEmpty(Queue* pq);

创建Queue.c文件,完成各个接口函数的实现:

#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"

// 初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

// 销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

// 入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newQNode = (QNode*)malloc(sizeof(QNode));
	if (newQNode == NULL)
	{
		perror("malloc fail");
		exit(-1);
	}
	newQNode->data = x;
	newQNode->next = NULL;
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newQNode;
	}
	else
	{
		pq->tail->next = newQNode;
		pq->tail = newQNode;
	}
}

// 出队列
void QueuePop(Queue* pq)
{
	assert(pq && !QueueEmpty(pq));
	QNode* next = pq->head->next;
	free(pq->head);
	if (next == NULL)
	{
		pq->tail = NULL;
	}
	pq->head = next;
}

// 取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq && !QueueEmpty(pq));
	return pq->head->data;
}

// 取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq && !QueueEmpty(pq));
	return pq->tail->data;
}

// 计算队列中有多少个数据
int QueueSize(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->head;
	int size = 0;
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

// 判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

在test.c中测试一下设计的队列结构和功能

#define _CRT_SECURE_NO_WARNINGS
#include "Queue.h"

void Test1()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	while (!QueueEmpty(&q))
	{
		printf("当前队列的大小为:%d\n", QueueSize(&q));
		printf("当前队头为:%d,队尾为:%d\n", QueueFront(&q), QueueBack(&q));
		printf("出队列……\n");
		QueuePop(&q);
	}
}

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

运行结果如下:

3 练习题

1 用队列实现栈(力扣)


2 用栈实现队列(力扣)

3 设计循环队列(力扣)

4 结语

队列的实现并非本篇提供的这一种方法,而是其中一种相对简单明了的方法,是结合了链表尾部只能知道其后的地址的特性,便于追加数据(入队列),而其他方法(如使用数组)多少都会更加麻烦一点。


所属分类专栏:数据结构与算法

上一节:【数据结构与算法从0到1 - C实现】四、栈的解析与实现

下一节:待续……

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值