当前位置: 首页 > news >正文

【数据结构】队列

目录

  • 一、队列
    • 1、概念与结构
    • 2、队列的实现
    • 3、队列的初始化
    • 4、打印队列数据
    • 5、入队
    • 6、销毁队列
    • 7、队列判空
    • 8、出队
    • 9、取队头、队尾数据
    • 10、队列中有效元素个数
  • 二、源码

个人主页,点击这里~
数据结构专栏,点击这里~
在这里插入图片描述

一、队列

1、概念与结构

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

入队列:进行插入操作的⼀端称为队尾
出队列:进行删除操作的⼀端称为队头
在这里插入图片描述
队列底层结构选择:队列可以用数组链表的结构实现,使用链表的结构实现更优⼀些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。所以我们将用链表实现队列这一数据结构。

2、队列的实现

我们要用链表实现队列,首先就要考虑先进先出这种模式下,链表该如何实现?
我们可以定义一个生成队列结点的结构体,生成一个结点看守队头,再生成一个结点看守队尾,这样我们访问队头和队尾的时候都会比较简单且节省时间,然后将这两个结点包装在一个队列结构体里面。

typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QN;
//队列的结构
typedef struct Queue
{
	QN* head;
	QN* tail;
}Queue;

3、队列的初始化

队列的初始化的时候,我们只需要将队列中的两个结点headtail置为空即可。

void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

4、打印队列数据

//队列打印
void QueuePrint(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

5、入队

队列入队的时候是从队尾入的。在入队之前,我们要定义一个新节点newNode储存新数据,再执行入队操作。

//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{
	//定义新节点
	QN* newNode = (QN*)malloc(sizeof(QN));
	if (newNode == NULL)
	{
		perror("malloc fail!");
		return 1;
	}
	newNode->data = x;
	newNode->next = NULL;
	//入队
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

测试代码:

#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
}

int main()
{
	test();

	return 0;
}

测试结果:
在这里插入图片描述

6、销毁队列

我们在单链表博客中实现销毁链表的时候,是一个节点一个节点进行销毁的,所以我们在队列的销毁过程也一样,另外在销毁队列的时候,我们要保存要销毁节点的下一个节点,以免找不到下一个节点。

//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;//指向队列的队头
	while (pcur)
	{
		QN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->head = pq->tail = NULL;
}

注意这里不用把pq也释放掉,因为pq是队列的结构体,它不是动态内存开辟出来的,只有节点才是动态内存开辟出来的,它们才需要释放。

销毁测试结果:
在这里插入图片描述
从右面可以看出所有的节点都被置为了空,队列已经完全被销毁了。

7、队列判空

我们接下来就要实现出队操作,在出队操作之前我们还需要判断队列是不是空,如果为空就不能进行出队列操作。

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

这里要用到bool类型,所以要包含头文件#include <stdbool.h>

8、出队

出队时我们要把第一个节点销毁掉

//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//队头和队尾相等,即只有一个节点
	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QN* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

9、取队头、队尾数据

取队头数据:

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

取队尾数据

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

10、队列中有效元素个数

//队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	//方法一:遍历队列(适用于不频繁使用数据个数的情况)
	QN* pcur = pq->head;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
	//方法二:在队列的结构体中添加变量size
	// 并在初始化时初始化为0,每当添加节点时就让它计数
	//(适用于频繁使用数据个数的情况)
	//return pq -> size;
}

测试代码

#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
	printf("%d",QueueSize(&plist));
	QueueDestroy(&plist);
}

int main()
{
	test();

	return 0;
}

测试结果:
在这里插入图片描述

二、源码

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;
}QN;
//队列的结构
typedef struct Queue
{
	QN* head;
	QN* tail;
}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列打印
void QueuePrint(Queue* pq);
//入队---队尾
void QueuePush(Queue* pq, QDataType x);
//销毁队列
void QueueDestroy(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//出队---队头
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列中有效元素个数
int QueueSize(Queue* pq);

Queue.c

#include "Queue.h"

//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}
//队列打印
void QueuePrint(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{
	//定义新节点
	QN* newNode = (QN*)malloc(sizeof(QN));
	if (newNode == NULL)
	{
		perror("malloc fail!");
		return 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 QueueDestroy(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;//指向队列的队头
	while (pcur)
	{
		QN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->head = pq->tail = NULL;
}
//队列判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}
//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//队头和队尾相等,即只有一个节点
	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QN* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}
//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->head->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}
//队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	//方法一:遍历队列(适用于不频繁使用数据个数的情况)
	QN* pcur = pq->head;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
	//方法二:在队列的结构体中添加变量size
	// 并在初始化时初始化为0,每当添加节点时就让它计数
	//(适用于频繁使用数据个数的情况)
	//return pq -> size;
}

test.c

#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
	printf("%d",QueueSize(&plist));
	QueueDestroy(&plist);
}

int main()
{
	test();

	return 0;
}

总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~

http://www.dtcms.com/a/98810.html

相关文章:

  • el-radio-group 中 el-radio-button value未能绑定上数值数据
  • 欢乐力扣:合并两个有序链表
  • Redis6数据结构之List类型
  • 25_闭包节流防抖
  • Gateway实战(三)、断言-时间、Cookie信息
  • 从零开始研发GPS接收机连载——16、接收天上卫星信号成功定位
  • Python之变量与数据类型总结
  • Linux C语言调用第三方库,第三方库如何编译安装
  • Android 12系统源码_输入系统(四)触摸异常问题排查
  • nginx 设置隐藏版本号
  • 【LangChain入门 9 Agent 】LangChain开发Agent智能体
  • 当模板方法模式遇上工厂模式:一道优雅的烹饪架构设计
  • 【Qt】ffmpeg编码—存储(H264)
  • 链路聚合技术
  • 基于Spring Boot的戒烟网站的设计与实现(LW+源码+讲解)
  • 每日OJ题_剑指offer数组篇(剑指offer04+剑指offer11+剑指offer21)
  • 深度学习 Deep Learning 第12章 深度学习的主流应用
  • python之kafka组件
  • 车辆vin码/车架号查询接口如何用Java实现
  • 数字内容体验提升用户参与策略
  • Java 求两个 List 集合的交集和差集
  • 交换综合实验
  • pycharm与python版本
  • 常见FPGA逻辑单元比较(仅参考)
  • 全国产1U机架式交换机解决方案
  • Linux: 进程间通信
  • Python 序列构成的数组(对序列使用+和_)
  • sqlmap基础命令总结
  • [C++] 智能指针 进阶
  • Mysql练习题