数据结构篇五:队列

news2024/11/16 21:25:48

文章目录

  • 前言
  • 1.队列
    • 1.1 队列的概念及结构
    • 1.2 队列的实现
  • 2. 各功能的解析及实现
    • 2.1 队列的创建
    • 2.2 初始化队列
    • 2.3 队尾入队列
    • 2.4 队头出队列
    • 2.5 获取队头元素
    • 2.6 获取队尾元素
    • 2.7 队列中有效元素个数
    • 2.8 检查队列是否为空
    • 2.9 销毁队列
  • 3.代码实现
    • 3.1 Queue.h
    • 3.2 Queue.c
    • 3.3 test.c
  • 4.总结

前言

  前面学习了栈,特点是后进先出,今天讲解的队列与其恰恰相反,是先进先出,先进来的数据先出去。

1.队列

1.1 队列的概念及结构

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

1.2 队列的实现

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

  1. front == NULL,tail = = NULL说明队列为空。
  2. 当插入第一个元素时front与tail同时指向第一个元素。
  3. 当入队列时,就是单链表的尾插。
  4. 当出队列时,就是单链表的头删。

2. 各功能的解析及实现

2.1 队列的创建

  我们采用链表的方式进行实现,因为如果用数组的话,在进行出队列操作时,需要将全部元素都往前移一位,实际上就是将第一个元素覆盖了,比较繁琐,效率没有链表高,所以此处采用链表的方式进行实现。

typedef int Datatype;
//链式结构表示队列
typedef struct QListNode
{
	Datatype data;
	struct Queue* next;
}QNode;

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

  我们定义了两个结构体,一个就是每个元素的内容,一个数据域以及一个指针域。另一个是指向元素的指针,因为在队列里需要用到两个,所以直接封装起来用比较好一些。同时封装到一个结构体内,我们就可以指针传递结构体的指针就可以了,就不需要传递单个(单独的front或者tail)的二级指针。(如果不太理解的话,可以将后续的代码与单链表这篇博客里面的代码进行比较观看,可以更好的理解为什么这里是传递一级指针,而单链表中传递的是二级指针。

2.2 初始化队列

  将指针置空即可,防止出现野指针问题。

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

2.3 队尾入队列

  每次入队列都开辟一个新空间存放元素,然后再进行尾插就完成了。需要注意的是当插入第一个元素的时候是将新结点的地址给了front与tail,而不是进行尾插。因此此时的front与tail是空指针,是不能对他们进行解引用的。

void QueuePush(Queue* pq, Datatype x)
{
	assert(pq);	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("开辟失败!!");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	
	if (pq->front == NULL)
	{
		pq->tail = newnode;
		pq->front = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = pq->tail->next;
	}
}

2.4 队头出队列

  删除队头元素,我们只需要保存第二个元素的地址,然后将队头元素的空间进行释放即可,然后再将front移到第二个元素的位置。需要注意的是,当队列中仅剩一个结点时,front与tail指向的是同一块空间,当对front进行释放时,tail也会被释放,如图:
在这里插入图片描述  此时tail就是一个野指针,指向一块已经被释放的空间,因此我们需要加一个判断,也就是当front == NULL时,tail也要被置为NULL。

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	QNode* next = pq->front->next;
	free(pq->front);
	pq->front = next;
	if (pq->front == NULL)
	{
		pq->tail = NULL;
	}
}

2.5 获取队头元素

  唯一需要注意的就是需要判断一下队列是否为空即可。

Datatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->front->data;
}

2.6 获取队尾元素

  唯一需要注意的就是需要判断一下队列是否为空即可。

Datatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

2.7 队列中有效元素个数

  从头遍历依次计算即可。

int QueueSize(Queue* pq)
{
	assert(pq);
	int count = 0;
	QNode* cur = pq->front;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

2.8 检查队列是否为空

  队列为空的状态就是front是空指针,没有指向元素。

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	if (pq->front == NULL)
	{
		return true;
	}
	return false;
}

2.9 销毁队列

  只需要提前保存准备释放结点的下一个结点地址,然后不断循环进行释放就可以了。

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->front;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->front = NULL;
	pq->tail = NULL;
}

3.代码实现

3.1 Queue.h

  包含队列的创建以及各个功能函数的声明。

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

typedef int Datatype;

//链式结构表示队列
typedef struct QListNode
{
	Datatype data;
	struct Queue* next;
}QNode;

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

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

// 队尾入队列
void QueuePush(Queue* pq, Datatype data);

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

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

// 获取队列队尾元素
Datatype QueueBack(Queue* pq);

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

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

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

3.2 Queue.c

  各个功能函数的实现。

#include"Queue.h"

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

void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->front;
	while (cur != NULL)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->front = NULL;
	pq->tail = NULL;
}

void QueuePush(Queue* pq, Datatype x)
{
	assert(pq);	
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("开辟失败!!");
		return;
	}
	newnode->next = NULL;
	newnode->data = x;
	
	if (pq->front == NULL)
	{
		pq->tail = newnode;
		pq->front = newnode;
	}
	else
	{
		pq->tail->next = newnode;
		pq->tail = pq->tail->next;
	}
}

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	QNode* next = pq->front->next;
	free(pq->front);
	pq->front = next;
	if (pq->front == NULL)
	{
		pq->tail = NULL;
	}
}


Datatype QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->front->data;
}

Datatype QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

int QueueSize(Queue* pq)
{
	assert(pq);
	int count = 0;
	QNode* cur = pq->front;
	while (cur)
	{
		count++;
		cur = cur->next;
	}
	return count;
}

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	if (pq->front == NULL)
	{
		return true;
	}
	return false;
}

3.3 test.c

  对队列功能的测试。

#include"Queue.h"

void test()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);//测试:入队列
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	printf("%d\n",QueueFront(&q));//测试:获取队头元素

	printf("%d\n", QueueSize(&q));//测试:统计元素个数

	QueueDestroy(&q);
}

void test1()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);//测试:入队列
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);

	while (!QueueEmpty(&q))
	{
		Datatype x = QueueFront(&q);
		printf("%d ", x);
		QueuePop(&q);   //测试:出队列
	}
	printf("\n");

	QueueDestroy(&q);
}
int main()
{
	test();
	//test1();
	return 0;
}

4.总结

  与栈差不多,一个是用数组实现的,一个是用链表实现的,一个是后进先出,一个先进先出。难度差距不大,重点依旧是放在做题方面感觉较好。
  那么队列的讲解就先告一段落了,如果发现文章哪里有问题可以在评论区提出来或者私信我嗷。接下来我会继续深入学习数据结构的其他知识,开启新的篇章,那么本期就到此结束,让我们下期再见!!觉得不错可以点个赞以示鼓励喔!!

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

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

相关文章

JavaWeb ( 七 ) JSTL Tag标签

2.5.JSTL标签与EL表达式 2.5.1.EL表达式 EL表达式 : Expression Language 目的&#xff1a;为了使JSP写起来更加简单 格式&#xff1a;${expression} EL 提供“.“和“[ ]“两种运算符来存取数据。${user.name}, ${user[“name”] }支持算术操作符, 关系操作符, 逻辑操作符…

Python:Python进阶:内存管理机制

Python内存管理机制 1. 堆2. 栈3. 引用4. Python中可变对象和不可变对象有个问题&#xff1a;你可以好好思考下总结 Python内存管理程序是用 C/C写的&#xff0c;这里我们以 CPython解释器为例说明。 在Python 中 所有数据类型 包括&#xff1a;int dict str都是一个对象&#…

层次分析法及找工作问题实战

学习知识要实时简单回顾&#xff0c;我把学习的层次分析法简单梳理一下&#xff0c;方便入门与复习。 AHP 层次分析法&#xff08;Analytic Hierarchy Process&#xff0c;简称 AHP&#xff09;是对一些较为复杂、较为模糊的问题作出决策的简易方法&#xff0c;它特别适用于那…

C++类和对象上

专栏&#xff1a;C/C 个人主页&#xff1a;HaiFan. 专栏简介&#xff1a;本章为大家带来C类和对象相关内容。 类和对象 前言面向过程和面向对象类的引入类的定义对于类中成员的命名建议 类的访问限定符及封装访问限定符封装 类的作用域类的实例化如何计算类对象的大小this指针t…

Web自动化测试——XAPTH高级定位

XAPTH高级定位 一、xpath 基本概念二、xpath 使用场景三、xpath 相对定位的优点四、xpath 定位的调试方法五、xpath 基础语法&#xff08;包含关系&#xff09;六、xpath 顺序关系&#xff08;索引&#xff09;七、xpath 高级用法1、[last()]: 选取最后一个2、[属性名属性值 an…

ESP32设备驱动-PCF8575IO扩展器驱动

PCF8575IO扩展器驱动 文章目录 PCF8575IO扩展器驱动1、PCF8575介绍2、硬件准备3、软件准备4、驱动实现1、PCF8575介绍 PCF8575用于两线双向总线 (I2C) 的 16 位 I/O 扩展器专为 2.5-V 至 5.5-V VCC 操作而设计。 PCF8575 器件通过 I2C 接口 [串行时钟 (SCL)、串行数据 (SDA)]…

flask教程8:模板

文章目录 一、模板与自定义过滤器1 模板2 过滤器转义过滤器讲解 3自定义过滤器 二、表单1表单2表单扩展 三、创建表单模型类与模板使用3.1 表单模型类 四 、使用表单接受并检验参数五、模板宏的使用六 、宏定义在外部的使用七 &#xff1a;模板继承与包含继承包含include 八 、…

PVE 安装 windows10

pve 安装教程大家可以参考视频&#xff1a;pve 安装 pve 安装 Windows10 视频教程&#xff1a;pve 安装Windows10 在安装好 pve 后我们就可以进行虚拟机的安装了。当然我们可以自行决定是否有必要进行 win10 的安装。 准备工作 1. 下载 win10 镜像文件&#xff1a;https://…

数据结构与算法基础(王卓)(35):交换排序之快排【第二阶段:标准答案、初步发现问题】

目录 第二阶段&#xff1a;一分为二 整个快排算法的程序运行大框架&#xff1a; 做出的改动&#xff08;和原来程序的区别&#xff09;&#xff1a; Project 1: PPT标准答案&#xff1a; Project 1小问题&#xff1a; Project 1还存在着一个巨大的问题&#xff1a; 具体问…

嵌入式软考备考_8 软件测试

软件测试 测试&#xff1a;在规定的条件下操作程序&#xff0c;以发现错误&#xff0c;对软件质量进行评估。 对象&#xff1a;程序&#xff0c;数据&#xff0c;文档。 目的&#xff1a;发现错误&#xff0c;看是否满足用户需求&#xff0c;发现错误产生的原因&#xff08;…

汇编四、51单片机汇编指令2

1、机器码 (1)MOV A,#0x60对应机器码为7460 (2)7460对应二进制 0111 0100 0110 0000 0x74对应指令&#xff0c;0x60对应立即数。 (3)immediate data翻译为立即数。 (4)可人为查表把汇编转为机器码&#xff0c;也可通过编译器把汇编转为机器码。 2、汇编常见缩写 (1)Rn: n可…

leetcode-040-组合总和2

题目及测试 package pid040; /* 40. 组合总和 II 给定一个候选人编号的集合 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的每个数字在每个组合中只能使用 一次 。注意&#xff1a;解集不能包含重复的组合…

Vue中使用EasyPlayer播放H265视频流

需求说明 需要在Vue2的项目中使用EasyPlayer进行H265视频流的播放。使用官方的最新版本加载H265会有问题。一直处于加载中… 实现步骤 引入easyplayer,这里最开始引入了最新版会有问题&#xff0c;因此引入的是3.3.12版本&#xff0c;可参照官方文档进行配置。 EasyPlayer示…

HBase整合Phoenix

HBase整合Phoenix 创建软件目录 mkdir -p /opt/soft cd /opt/soft下载软件 wget https://dlcdn.apache.org/phoenix/phoenix-5.1.3/phoenix-hbase-2.5-5.1.3-bin.tar.gz解压 hbase tar -zxvf phoenix-hbase-2.5-5.1.3-bin.tar.gz修改 hbase 目录名称 mv phoenix-hbase-2.5…

(初)进程概念

目录 认识冯诺依曼系统 操作系统(Operator System) 设计OS的目的&#xff1a; 定位&#xff1a; 如何理解管理&#xff1a; 总结&#xff1a; 系统调用和库函数概念&#xff1a; 进程 基本概念 &#xff1a; 描述进程PCB task_struct - PCB的一种 task_struct内容分…

编译安装最新的Linux系统内核

现在还有不少机器是CentOS8 Stream系统&#xff0c;虽然上了贼船&#xff0c;不影响用就是了。8的编译和7大同小异&#xff0c;只是踩了更多的坑在这里记录一下&#xff0c;或许会帮到看到的朋友。 安装编译环境 CentOS8安装必要的包 yum groupinstall "Development Too…

【P13】JMeter 常数吞吐量定时器(Constant Throughput Timer)

文章目录 1、基于计算吞吐量&#xff1a;只有此线程2、基于计算吞吐量&#xff1a;所有活动线程3、基于计算吞吐量&#xff1a;当前线程组中的所有活动线程4、基于计算吞吐量&#xff1a;所有活动线程&#xff08;共享&#xff09;5、基于计算吞吐量&#xff1a;当前线程组中的…

【2023/05/08】雅卡尔织布机

Hello&#xff01;大家好&#xff0c;我是霜淮子&#xff0c;2023倒计时第3天。 Share The world puts off its mask of vastness to its lover. It becomes small as one song,as one kiss of the eternal. 译文&#xff1a; 世界对着它的爱人&#xff0c;把它浩瀚的面具揭…

已做过算法题总结2

20. 有效的括号 (括号匹配是使用栈解决的经典问题&#xff0c;这道题主要是记住三种不成立的情况) 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串&#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用…

解密链表元素移除:三种巧妙思路,轻松驱逐难缠结点

本篇博客会讲解力扣“203. 移除链表元素”的解题思路&#xff0c;这是题目链接。 老规矩&#xff0c;先来审题&#xff1a; 以下是一些示例&#xff1a; 以下是提示&#xff1a; 本题的思路还挺多的&#xff0c;不过都是链表的常规操作。 思路1 万能的尾插法。遍历链表&am…