数据结构之队列的超详细讲解

目录

前言

1.队列的概念

2.队列的结构

3.队列的实现

3.1队列结构的实现

3.2队列操作函数的声明

3.3队列中方法的实现

3.3.1队列的初始化

3.3.2队列的销毁

3.3.3队列的尾插

3.3.4队列的头删

3.3.5取队头的数据

3.3.6取队尾的数据

3.3.7队列中的元素个数

3.3.8队列是否为空

4.测试

5.总结

6.完整代码

Queue.h

Queue.c

text.c


前言

我们之前实现了栈这个数据结构,今天我们就对队列这个数据结构进行讲解和实现.如果对栈这个结构还不是很了解的话,大家可以先去看--数据结构之栈的超详细讲解-CSDN博客,后面我会发布栈和队列的经典面试题,如果感兴趣的宝子们赶紧点赞关注收藏起来吧!

1.队列的概念

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

这里与栈的区分:

栈的入栈和出栈只能在栈顶

队列的入队在队尾,出队在队头

栈:后进先出

队列:先进先出

2.队列的结构

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数 组头上出数据,效率会比较低。

我们的头节点为队头,方便数据的删除

我们的尾节点位队尾,方便数据的插入

3.队列的实现

3.1队列结构的实现

这里我们使用链表的结构实现队列

cpp 复制代码
typedef int QDateType;

//节点结构体
typedef struct QueueNode
{
	struct QueueNode* next;
	QDateType val;
}QNode;

这样会有些麻烦,因为在对链表进行插入删除操作时需要使用二级指针,详细讲解可以看我之前的单链表专题--单链表专题-CSDN博客,所以这里我么在额外定义一个结构体

两者对比如下:

cpp 复制代码
//队尾插入
//void QueuePush(QNode** phead, QNode** ptail, QDateType x);
//void QueuePop(QNode** phead, QNode** ptail);

//队尾插入
void QueuePush(QNode* pq, QDateType x);
//队头删除
void QueuePop(QNode* pq);
cpp 复制代码
//避免使用二级指针
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

这里直接将我们头尾指针和队中元素个数size存储在结构体queue中

3.2队列操作函数的声明

下面是队列操作函数的声明:

cpp 复制代码
//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//队尾插入
//void QueuePush(QNode** phead, QNode** ptail, QDateType x);
//void QueuePop(QNode** phead, QNode** ptail);

//队尾插入
void QueuePush(QNode* pq, QDateType x);
//队头删除
void QueuePop(QNode* pq);

//取队头队尾的数据
QDateType QueueFront(Queue* pq);
QDateType QueueBack(Queue* pq);

//队中的元素个数
int QueueSize(Queue* pq);

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

3.3队列中方法的实现

3.3.1队列的初始化

不要忘了断言

cpp 复制代码
//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

3.3.2队列的销毁

这里将链表的头节点一个一个删除,不要忘了最后将头尾指针指为NULL,还有size也要赋值为0

cpp 复制代码
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}

3.3.3队列的尾插

要想插入数据,就得动态开辟空间,这与单链表,栈是一样的,用malloc动态开辟空间

开辟完空间后,先判断链表是否为空,为空的话,将头节点和尾节点等于我们新建的节点

如果不是,直接尾插

cpp 复制代码
//队尾插入
void QueuePush(Queue* pq, QDateType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}

3.3.4队列的头删

删除时注意需要多加一句断言:pq->size != 0

然后就是熟悉的两种情况:

1.只有一个节点时:(没有节点的情况我们断言已经排除掉了)

直接free当前节点

2.有多个节点

一个一个free

cpp 复制代码
//从队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	//一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
		pq->size = 0;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;

		pq->size--;
	}
	
}

3.3.5取队头的数据

因为我们定义了头指针和尾指针,所以这两个操作十分简单

cpp 复制代码
QDateType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

3.3.6取队尾的数据

cpp 复制代码
QDateType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}

3.3.7队列中的元素个数

队列中元素的个数即为我们size的值

cpp 复制代码
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}

3.3.8队列是否为空

因为返回类型为布尔类型,所以我们返回size与0比较的布尔值

如果为空返回true

反之返回 false

cpp 复制代码
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}

4.测试

我们测试将一些数据插入队列,并循环打印出队列中的数,观察队列的先进先出的性质

测试代码如下:

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


#include "Queue.h"

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	printf("%d \n", QueueFront(&q));
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return 0;
}

结果如下:

5.总结

队列数据结构的实现其实并不难,随着栈和队列数据结构实现的完结,后面经典OJ例题可少不了,我会及时更新,感兴趣的的宝子们赶紧点赞关注收藏起来吧!

6.完整代码

Queue.h

cpp 复制代码
#pragma once

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

typedef int QDateType;

//节点结构体
typedef struct QueueNode
{
	struct QueueNode* next;
	QDateType val;
}QNode;

//避免使用二级指针
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

//初始化
void QueueInit(Queue* pq);
//销毁
void QueueDestroy(Queue* pq);

//队尾插入
//void QueuePush(QNode** phead, QNode** ptail, QDateType x);
//void QueuePop(QNode** phead, QNode** ptail);

//队尾插入
void QueuePush(QNode* pq, QDateType x);
//队头删除
void QueuePop(QNode* pq);

//取队头队尾的数据
QDateType QueueFront(Queue* pq);
QDateType QueueBack(Queue* pq);

//队中的元素个数
int QueueSize(Queue* pq);

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

Queue.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1

#include "Queue.h"

//初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);

		cur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}


//队尾插入
void QueuePush(Queue* pq, QDateType x)
{
	assert(pq);

	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail!");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;

	if (pq->phead == NULL)
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;
}


//从队头删除
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->size != 0);

	//一个节点
	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
		pq->size = 0;
	}
	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;

		pq->size--;
	}
	
}

QDateType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);

	return pq->ptail->val;
}

QDateType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->phead);

	return pq->phead->val;
}

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

	return pq->size;
}

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

	return pq->size == 0;
}

text.c

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS 1


#include "Queue.h"

int main()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	printf("%d \n", QueueFront(&q));
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
	return 0;
}
相关推荐
CSCN新手听安1 小时前
list的常用操作
数据结构·list
梅茜Mercy3 小时前
数据结构:链表(经典算法例题)详解
数据结构·链表
88号技师3 小时前
2024年12月一区SCI-加权平均优化算法Weighted average algorithm-附Matlab免费代码
人工智能·算法·matlab·优化算法
IT猿手3 小时前
多目标应用(一):多目标麋鹿优化算法(MOEHO)求解10个工程应用,提供完整MATLAB代码
开发语言·人工智能·算法·机器学习·matlab
青春男大3 小时前
java栈--数据结构
java·开发语言·数据结构·学习·eclipse
88号技师3 小时前
几款性能优秀的差分进化算法DE(SaDE、JADE,SHADE,LSHADE、LSHADE_SPACMA、LSHADE_EpSin)-附Matlab免费代码
开发语言·人工智能·算法·matlab·优化算法
Zer0_on3 小时前
数据结构栈和队列
c语言·开发语言·数据结构
一只小bit3 小时前
数据结构之栈,队列,树
c语言·开发语言·数据结构·c++
马浩同学4 小时前
【GD32】从零开始学GD32单片机 | DAC数模转换器 + 三角波输出例程
c语言·单片机·嵌入式硬件·mcu
我要学编程(ಥ_ಥ)4 小时前
一文详解“二叉树中的深搜“在算法中的应用
java·数据结构·算法·leetcode·深度优先