【数据结构初阶】六、线性表中的队列(链式结构实现队列)

news2024/11/24 7:52:27

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

【数据结构初阶】五、线性表中的栈(顺序表实现栈)_高高的胖子的博客-CSDN博客

 =========================================================================

                     

1 . 队列(Queue)

队列的概念和结构:

队列的概念

  • 队列是一种只允许在一端执行插入数据操作另一端进行删除数据操作特殊线性表
                      
  • 入队列 -- 进行插入操作的一端称为队尾
    出队列 -- 进行删除操作的一端称为队头

                
  • 队列中的数据元素遵守
    先进先出FIFO -- First In First Out)的原则 -- 先进入的元素会先出来
    所以可以应用在公平性排队(抽号机)、BFS(广度优先遍历)
                        

队列的结构

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

2 . 队列的实现

                

使用 顺序表(数组)链表(链式结构) 都可以实现队列

使用顺序表的话,入队列比较简单,但在出队列时需要删除和挪动数据效率较低

所以下面用链表(链式结构)实现队列  --  单向 + 无头 + 非循环 链表

入队 -- 单链表尾部插入(尾插)         ;      出队 -- 单链表头部删除(头删)

               

(详细解释在图片的注释中,代码分文件放下一标题处)

               

实现具体功能前的准备工作

  • 定义队列(链式结构)中数据域存储的数据类型
                               
  • 定义队列(链式结构)结点类型
    包含 队列指针域 队列数据域
                 
  • 定义队列类型
    包含 头结点指针尾结点指针 和 队列结点(元素)个数
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueInit函数 -- 将队列进行初始化

  • assert断言队列类型指针不为空
                               
  • 队头结点置为空
    队尾结点置为空
    队列结点(元素)个数置为0
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueDestroy函数 -- 将队列销毁

  • assert断言队列类型指针不为空
                               
  • 创建一个在队列进行遍历的指针cur
    使用while循环进行遍历释放队列结点
                 
  • 结点都释放后,把队头队尾指针都置空
                   
  • 再把队列结点(元素)个数置为0
图示

            

            

---------------------------------------------------------------------------------------------

            

QueuePush函数 -- 用链表的尾插操作实现入队

  • assert断言队列类型指针不为空
                               
  • 为队列结点开辟动态空间检查空间开辟情况
                 
  • 结点开辟成功
    尾插值(x)赋给队列结点的数据域并将指针域置为空
                   
  • 空间开辟后进行尾插
    如果队列刚初始化队列为空,将刚开辟的结点newnode地址赋给头尾结点指针
    队列不为空正常进行尾插操作
                
  • 插入数据后队列结点(元素)个数++
图示

            

            

---------------------------------------------------------------------------------------------

            

QueuePop函数 -- 用链表的头删操作实现出队

  • assert断言队列类型指针不为空队列不为空
                               
  • 出队(头删)分两种情况
    队列中只剩一个结点 -- 头删后头指针移动尾指针也要移动
    队列不止一个结点 -- 头删后只需移动队头结点
                 
  • 删除队列结点(元素)个数--
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueFront函数 -- 返回队头结点的数据域数据

  • assert断言队列类型指针不为空队列不为空
                               
  • 队列有数据,则直接返回队头结点数据域数据
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueBack函数 -- 返回队尾结点的数据域数据

  • assert断言队列类型指针不为空队列不为空
                               
  • 队列有数据,则直接返回队尾结点数据域数据
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueEmpty函数 -- 判断队列是否为空

  • assert断言队列类型指针不为空
                               
  • 直接判断队头结点指向的下个结点是否为空直接返回判断结果
图示

            

            

---------------------------------------------------------------------------------------------

            

QueueSize函数 -- 判断队列结点(元素)个数

  • assert断言队列类型指针不为空
                               
  • 直接返回size队列结点(元素)个数
图示

            

            

---------------------------------------------------------------------------------------------

            

总体测试:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

3 . 对应代码

Queue.h -- 队列头文件

#pragma once

//包含之后需要的头文件:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

//以链表(链式结构)实现队列:
//双向+循环 的链表可以解决找尾结点的问题,
//定义一个尾指针也可以解决该问题,
//哨兵位 可以解决二级指针的问题,
//且尾插时可以少一层判断,但还有方法可以解决


//定义队列(链式结构)中数据域存储的数据类型:
typedef int QDataType;

//定义队列(链式结构)结点类型:
typedef struct QueueNode
{
	//队列指针域:
	struct QueueNode* next;

	//队列数据域:
	QDataType data;

}QNode; //将类型重命名为Qnode


//定义队列类型:
typedef struct Queue
{
	//因为用链表尾插实现入队,
	//用链表头删实现出队,
	//那么就需要头结点和尾结点的指针,
	//所以可以直接将这两个指针封装为一个类型,
	//队列类型:

	//头结点指针:
	QNode* head;

	//尾结点指针:
	QNode* tail;

	//记录队列结点(元素)个数:
	int size; 

	//这样之后在出队和入队操作时,
	//就不需要用到二级指针,
	//直接接收这个结构体指针,
	//通过结构体指针运用结构体里的头尾结点指针,
	//再用头尾结点指针定义头尾结点
	//来实现 二级指针、带哨兵位头结点 和 返回值 的作用
	//所以现在已知的通过指针定义结点的方法就有4种:
	//		1. 结构体包含结点指针
	//		2. 二级指针调用结点指针
	//		3. 哨兵位头结点指针域next指向结点地址
	//		4. 返回值返回改变的结点指针

}Que; //重命名为Que


//队列初始化函数 -- 将队列进行初始化
//接收队列类型指针(包含链表头尾结点) 
void QueueInit(Que* pq);

//队列销毁函数 -- 将队列销毁
//接收队列类型指针(包含链表头尾结点) 
void QueueDestroy(Que* pq);

//队列入队函数 -- 用链表的尾插操作实现入队
//接收队列类型指针(包含链表头尾结点) 、尾插值
void QueuePush(Que* pq, QDataType x);

//队列出队函数 -- 用链表的头删操作实现出队
//接收队列类型指针(包含链表头尾结点) 
void QueuePop(Que* pq);

//队头函数 -- 返回队头结点的数据域数据
//接收队列类型指针(包含链表头尾结点) 
QDataType QueueFront(Que* pq);

//队尾函数 -- 返回队尾结点的数据域数据
//接收队列类型指针(包含链表头尾结点) 
QDataType QueueBack(Que* pq);

//判空函数 -- 判断队列是否为空
//接收队列类型指针(包含链表头尾结点) 
bool QueueEmpty(Que* pq);

//队列大小函数 -- 判断队列结点(元素)个数
//接收队列类型指针(包含链表头尾结点) 
int QueueSize(Que* pq);

            

            

---------------------------------------------------------------------------------------------

                

Queue.c -- 队列函数实现文件

#define _CRT_SECURE_NO_WARNINGS 1

//包含队列头文件:
#include "Queue.h"

//队列初始化函数 -- 将队列进行初始化
//接收队列类型指针(包含链表头尾结点) 
void QueueInit(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);

	//将队头结点置为空:
	pq->head = NULL;

	//将队尾结点置为空:
	pq->tail = NULL;

	//队列结点(元素)个数置为0:
	pq->size = 0;
}



//队列销毁函数 -- 将队列销毁
//接收队列类型指针(包含链表头尾结点) 
void QueueDestroy(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);

	//释放队列跟单链表的释放一样
	//先创建一个在队列进行遍历的指针:
	QNode* cur = pq->head; //从队头结点开始

	//使用while循环进行遍历释放队列结点:
	while (cur != NULL)	
	{
		//先保存下个结点:
		QNode* next = cur->next;

		//再释放当前结点:
		free(cur);

		//再指向下个结点:
		cur = next;
	}

	//结点都释放后,把队头队尾指针都置空:
	pq->head = NULL;
	pq->tail = NULL;

	//再把队列结点(元素)个数置为0:
	pq->size = 0;
}



//队列入队函数 -- 用链表的尾插操作实现入队
//接收队列类型指针(包含链表头尾结点) 、尾插值
void QueuePush(Que* pq, QDataType x)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);

	//入队放入元素需要空间,
	//所以要先为队列结点开辟动态空间:
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	//检查是否开辟成功:
	if (newnode == NULL)
	{
		//开辟失败则打印错误信息:
		perror("malloc fail");
		//终止程序:
		exit(-1);
	}

	//队列结点完成后将尾插值(x)
	//赋给队列结点数据域:
	newnode->data = x;
	//指针域指向空:
	newnode->next = NULL;

	//空间开辟后进行尾插:
	if (pq->tail == NULL)
		//如果队列刚初始化,队列为空,
		//头结点指针和尾结点指针都为空:
	{
		//那么将刚开辟的结点newnode地址
		//赋给头结点指针和尾结点指针
		pq->head = newnode;
		pq->tail = newnode;
	}
	else
		//队列不为空,进行尾插:
	{
		//将目前队尾结点指针域next指向尾插结点:
		pq->tail->next = newnode;
		//然后再指向尾插结点,成为新队尾结点:
		pq->tail = newnode;
	}

	//插入数据后队列结点(元素)个数++:
	pq->size++;
}



//队列出队函数 -- 用链表的头删操作实现出队
//接收队列类型指针(包含链表头尾结点) 
void QueuePop(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);
	//assert断言队列不为空,没数据不能删除:  
	assert(QueueEmpty != true); //不为空就继续程序

	//如果队列中只剩一个结点:
	if (pq->head->next == NULL)
		//队头指针指向空,说明只剩一个结点,
		//只剩一个结点说明队头队尾指针都指向这一个结点,
		//所以这时头删后头指针移动,尾指针也要移动
	{
		//先释放("删除")队列目前头结点:
		free(pq->head);

		//删除后将队头队尾指针都置为空:
		pq->head = NULL;
		pq->tail = NULL;
	}
	else
		//队列不止一个结点,则头删后只需移动队头结点:
	{
		//用链表的头删操作实现出队,
		//先保存第二个结点地址:
		QNode* next = pq->head->next;

		//释放("删除")队列目前头结点:
		free(pq->head);

		//再将队头结点指针指向原本第二个结点next,
		//让其成为新的队头结点:
		pq->head = next;
	}

	//“删除”后队列结点(元素)个数--:
	pq->size--; 
}



//队头函数 -- 返回队头结点的数据域数据
//接收队列类型指针(包含链表头尾结点) 
QDataType QueueFront(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);
	//assert断言队列不为空,没数据不能查找:  
	assert(QueueEmpty != true); //不为空就继续程序

	//队列有数据,则直接返回队头结点数据域数据:
	return pq->head->data;
}



//队尾函数 -- 返回队尾结点的数据域数据
//接收队列类型指针(包含链表头尾结点) 
QDataType QueueBack(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);
	//assert断言队列不为空,没数据不能查找:  
	assert(QueueEmpty != true); //不为空就继续程序

	//队列有数据,则直接返回队尾结点数据域数据:
	return pq->tail->data;
}



//判空函数 -- 判断队列是否为空
//接收队列类型指针(包含链表头尾结点) 
bool QueueEmpty(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);

	//直接判断队头结点指向的下个结点是否为空:
	return pq->head == NULL; 
	//是则返回true -- 队列为空
	//是则返回false -- 队列不为空
}


//队列大小函数 -- 判断队列结点(元素)个数
//接收队列类型指针(包含链表头尾结点) 
int QueueSize(Que* pq)
{
	//assert断言队列类型指针不为空:
	assert(pq != NULL);

	//直接返回size队列结点(元素)个数:
	return pq->size;
}

            

            

---------------------------------------------------------------------------------------------

                

Test.c -- 队列测试文件

#define _CRT_SECURE_NO_WARNINGS 1

//包含队列头文件:
#include "Queue.h"

//队列测试函数:
void TestQueue()
{
	//创建队列类型:
	Que q;

	//对队列类型进行初始化:
	QueueInit(&q);

	//进行入队操作:
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);

	//当前队尾值:
	printf("当前队尾值:%d\n", QueueBack(&q));

	//当前队列元素个数:
	printf("当前队列元素个数:%d\n", QueueSize(&q));

	//换行:
	printf("\n");

	//使用while循环遍历进行出队:
	//(类似抽号机,当前号抽完就到下个号)
	while (!QueueEmpty(&q))
		//队列不为空就继续出队:
	{
		//打印出队值:
		printf("当前出队值为:%d\n", QueueFront(&q));
		//进行出队:
		QueuePop(&q); //出队后打印下个出队值
	}

	//换行:
	printf("\n");

	//当前队列元素个数:
	printf("当前队列元素个数:%d", QueueSize(&q));

	//销毁队列:
	QueueDestroy(&q);
}

//主函数:
int main()
{
	//调用队列测试函数:
	TestQueue();

	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1050076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Java的食堂管理系统设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言系统功能具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域…

【设计模式】备忘录模式

文章目录 1.备忘录模式定义2.备忘录模式的角色3.备忘录模式实现3.1.场景说明3.2.结构类图3.3.代码实现 4.备忘录模式优缺点5.备忘录模式适用场景6.备忘录模式总结 主页传送门&#xff1a;&#x1f481; 传送 1.备忘录模式定义 备忘录&#xff08;Memento Pattern&#xff09;模…

图像处理: 马赛克艺术

马赛克 第一章 马赛克的历史渊源 1.1 马赛克 艺术中的一种表面装饰&#xff0c;由紧密排列的、通常颜色各异的小块材料&#xff08;如石头、矿物、玻璃、瓷砖或贝壳&#xff09;组成。与镶嵌不同的是&#xff0c;镶嵌是将要应用的部件放置在已挖空以容纳设计的表面中&#xff0…

ubuntu20安装nvidia驱动

1. 查看显卡型号 lspci | grep -i nvidia 我的输出&#xff1a; 01:00.0 VGA compatible controller: NVIDIA Corporation GP104 [GeForce GTX 1080] (rev a1) 01:00.1 Audio device: NVIDIA Corporation GP104 High Definition Audio Controller (rev a1) 07:00.0 VGA comp…

国庆周《Linux学习第三课》

国庆周《Linux学习第三课》 国庆周《Linux学习第二课》_IHOPEDREAM的博客-CSDN博客 总结 用户的管理 增加一个用户 删除一个用户 修改一个用户 查看一个用户 用户组的管理 增加一个组 删除一个组 修改一个组 查看一个组 将用户成员增加到该组中去 移除组的成员 1 用户

怎样在CSDN插入代码块 怎么变色?

添加代码块&#xff0c;通常有三种方式&#xff1a; 文章目录 ①点击 工具栏中的代码块 代码块 </>&#xff0c;② 快捷键 ctrlshiftk③ 先粘贴上代码&#xff0c;在选中 ctrlshiftk4 如果代码没有变彩色 ①点击 工具栏中的代码块 代码块 </>&#xff0c; 例如 选…

UWB技术在汽车智能制造的应用

返修区车辆管理项目 应用背景 在车辆总装生产线中&#xff0c;车辆下线后检测与返修是最后一个关键环节&#xff0c;整车一旦下线&#xff0c;由于流水线装配工艺、来料等原因&#xff0c;可能会出现部分整车存在瑕疵&#xff0c;进而进入返修区域待检。由于可能出现问题的不确…

探索设计模式:从组合到享元的软件架构之旅 (软件设计师笔记)

&#x1f600;前言 设计模式是软件开发中常见和经常使用的一种最佳实践方式&#xff0c;它们是为了解决在软件设计中反复出现的一类问题而提出的通用解决方案。本文主要探讨了四种设计模式&#xff1a;Composite&#xff08;组合&#xff09;、Decorator&#xff08;装饰器&…

Passper for Excel v3.7.3.4 Excel 密码恢复工具

网盘下载 Passper for Excel 是 Windows 上一款 Excel 密码恢复工具。可以恢复任何丢失或忘记的 Excel 文档密码。具有高恢复率和 100% 的数据安全性。支持通过 GPU 加速恢复密码&#xff0c;恢复速度提高 10 倍以上。 字典&#xff0c;根据内置或自定义词典恢复密码。 一般来…

【接口测试】测试基础

一、接口测试简介 1.接口测试的定义 接口测试是一种测试系统组件间接口的测试&#xff0c;主要目标是检测外部系统与系统之间以及内部各个子系统之间的交互点&#xff0c;以检查数据的交换、传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系等。 随着前后端分离…

Visio如何对文本打下标、上标,以及插入公式编辑器等问题(已解决)

解决这个问题的本质问题&#xff0c;就是在Visio中插入公式编辑器&#xff08;这不是visio的常用命令&#xff0c;需要添加&#xff09;。 打开Visio--》文件--选项 点击选项&#xff0c;弹出对话框。在自定义功能区中&#xff0c;点击 常用命令&#xff0c;在下拉选项中&#…

【Ambari】银河麒麟V10 ARM64架构_安装Ambari2.7.6HDP3.3.1问题总结

&#x1f341; 博主 "开着拖拉机回家"带您 Go to New World.✨&#x1f341; &#x1f984; 个人主页——&#x1f390;开着拖拉机回家_大数据运维-CSDN博客 &#x1f390;✨&#x1f341; &#x1fa81;&#x1f341; 希望本文能够给您带来一定的帮助&#x1f338;文…

IDEA Debug技巧大全,看完就能提升工作效率

作者简介 目录 1.行断点 2.方法断点 3.异常断点 4.字段断点 5.条件表达式 1.行断点 行断点就是平时我们在代码行旁边单击鼠标打上的断点&#xff0c;这个没有什么好说的。关键点在于很多人不知道的&#xff0c;行断点其实是可以右击选择是对改行的全部调用都生效&#xf…

如何在Proteus进行STM32F103C8T6模拟以及keil5开发

一、下载Proteus 8和keil5 最新版 Proteus 8.15 Professional 图文安装教程&#xff08;附安装包&#xff09;_proteus密钥_main工作室的博客-CSDN博客Keil uVision5 5.38官方下载、安装及注册教程_keil uvision5下载_这是乐某的博客-CSDN博客 二、新建STM32F103C8项目 接下来…

计算协方差矩阵df.cov()

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 计算协方差矩阵 df.cov() [太阳]选择题 下列选项对代码运行结果描述错误的是&#xff1f; import pandas as pd df pd.DataFrame({ A: [1, 2, 3, 4, 5], B: [5, 4, 3, 2, 1] }) prin…

二维码智慧门牌管理系统:智能生活的新选择

文章目录 前言一、地址的唯一性与规范性二、智能化生活三、智能家居的融合四、广泛的应用场景 前言 在科技飞速发展的今天&#xff0c;智能化已经渗透到我们生活的各个层面。而在我们日常生活中&#xff0c;门牌号码的识别和管理是一个看似平凡但却非常重要的环节。为了更好地…

源码编译安装pkg-config

安装环境&#xff1a;银河麒麟 1 到这个网址下载pkg-config源码&#xff1a; Index of /releases (pkg-config.freedesktop.org) 2 解压 3 进入解压后的目录。输入 ./configure 但是报错。 4 根据报错信息&#xff0c;将configure改为&#xff1a; ./configure --with-i…

怒刷LeetCode的第19天(Java版)

目录 第一题 题目来源 题目内容 解决方法 方法一&#xff1a;遍历一次数组 方法二&#xff1a;贪心算法 方法三&#xff1a;双指针 第二题 题目来源 题目内容 解决方法 方法一&#xff1a;动态规划 方法二&#xff1a;贪婪算法 方法三&#xff1a;正则表达式 第…

基础算法--KMP字符串

KMP 算法是一个快速查找匹配串的算法&#xff0c;它的作用其实就是本题问题&#xff1a;如何快速在「原字符串」中找到「匹配字符串」。 在朴素解法中&#xff0c;不考虑剪枝的话复杂度是 O(m∗n) 的&#xff0c;而 KMP 算法的复杂度为 O(mn)。 KMP 之所以能够在O(mn) 复杂度内…

成都优优聚公司是靠谱的吗?

成都优优聚公司专业美团代运营团队&#xff0c;以高效、专业、全面的服务赢得了众多客户的青睐。作为一家在美团代运营行业具备丰富经验和优质资源的公司&#xff0c;我们始终以客户的需求为导向&#xff0c;致力于为客户打造出色的美团线上运营方案。 我们公司拥有一支经验丰富…