数据结构与算法(C语言版)P6---队列

news2025/7/19 7:03:00

1、队列的概念及结构

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

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

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

在这里插入图片描述
数据存放:现在有数据:A、B、C、D要存放,那存放数据顺序就是一次存入,并且存放的位置如下:
在这里插入图片描述

1.1、队列的实现概念

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

因为数组队列在处一个数据后们需要整体挪动数据向前,所以麻烦,这里不如链表。而链表在出数据是,把节点释放,然后将phead在指向要出数据的后一个数据即可,并且我们也记录个尾节点ptail,用来链接放在队列中新数据即可。

在这里插入图片描述
所以队列的实现主要以链表队列为主要。

2、链队列的实现

队列主要有以下接口函数

  • 初始化(QueueInit)
  • 销毁(QueueDestroy)
  • 扩容(BuyQueueNode)
  • 队尾插入数据(QueuePush)
  • 对头删除数据(QueuePop)
  • 取队尾数据(QueueBack)
  • 取对头数据(QueueFront)
  • 统计队列中数据个数(QueueSize)
  • 判断队列是否为空(QueueEmpty)

2.1、定义结构体

这里需要重点说明这个结构体的定义。
1、首先我们定义一个QueueNode结构体,这个结构体就是动态扩容的结点。里面有一个Next指针域,还有一个data数据域。如下:

typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

2、然后是重点来了:这里我们需要创建两个QueueNode*类型的指针:head,tail。分别用来队列的头结点和尾节点。如下:

typedef struct Queue
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

然后整体结构体定义就如下:

#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
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

2.2、队列初始化

将头结点和尾节点置为NULL即可。

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

2.3、销毁

依次遍历结点进行销毁

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

2.4、扩容

//扩容
QNode* BuyQueueNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->Next = NULL;
	return newnode;
}

2.5、队尾插入数据

队列在队尾入数据,因为我们提前设置了尾结点(tail),那操作就容易了,先将tail的Next域指向新节点的地址,然后再将新节点的地址赋值给tail,便于下次再尾插。

但是由于我们没创建哨兵位,所以在进行结点的插入和删除时,需要考虑边界问题。
那这里,特殊情况就是,如果是第一次插入数据,也就是当pq->head == NULL时,我们可以直接将新节点的地址赋值给头结点和尾节点。

//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = BuyQueueNode(x);

	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->Next = newnode;
		pq->tail = newnode;
	}
}

2.6、对头删除数据

核心思想:先保存头结点的后驱结点的地址,然后释放头结点,然后将先前保存头结点的后驱结点的地址赋值给头结点。
特殊情况:当pq->head == NULL时,也就意味着整个队列为空了,所以我们还需要将pq->tail = NULL。

//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->Next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

2.7、取队尾数据

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

2.8、取对头数据

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

2.8、统计队列元素个数

使用个计数器,依次遍历队列中的结点,来统计队列元素个数。

//统计队列元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	int count = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		count++;
		cur = cur->Next;
	}
	return count;
}

2.10、判断队列是否为空

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

2.11、main函数测试程序

下面测试程序功能:遍历输出队列中的数据。

#include "queue.h"

int main()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	QueuePush(&q, 6);

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

	return 0;
}

输出:
在这里插入图片描述

3、全代码展示

这里使用三个文件:

  • queue.h:用于结构体、各种函数接口的声明
  • queue.c:用于各种函数接口的定义。
  • test.c:用于创建链表,实现链表。

3.1、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
{
	struct QueueNode* head;
	struct QueueNode* tail;
}Queue;

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

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

//扩容
QNode* BuyQueueNode(QDataType x);

//对尾插入数据
void QueuePush(Queue* pq, QDataType x);

//队头删除数据
void QueuePop(Queue* pq);

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

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

//统计队列元素个数
int QueueSize(Queue* pq);

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

3.2、queue.c

#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)
	{
		QNode* next = cur->Next;
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

//扩容
QNode* BuyQueueNode(QDataType x)
{
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\n");
		exit(-1);
	}
	newnode->data = x;
	newnode->Next = NULL;
	return newnode;
}

//对尾插入数据
void QueuePush(Queue* pq, QDataType x)
{
	QNode* newnode = BuyQueueNode(x);

	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->Next = newnode;
		pq->tail = newnode;
	}
}

//队头删除数据
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	QNode* next = pq->head->Next;
	free(pq->head);
	pq->head = next;
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

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

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

//统计队列元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	int count = 0;
	QNode* cur = pq->head;
	while (cur)
	{
		count++;
		cur = cur->Next;
	}
	return count;
}

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

3.3、test.c

#include "queue.h"

int main()
{
	Queue q;
	QueueInit(&q);

	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	QueuePush(&q, 6);

	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n");

	QueueDestroy(&q);

	return 0;
}

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

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

相关文章

计算机网络 第四章:网络层

一.网络层概述 1.1分组转发和路由选择 网络层的主要任务就是将分组从源主机经过多个网络和多段链路传输到目的主机&#xff0c;可以将该任务划分为分组转发和路由选择两种重要的功能。 如图所示&#xff1a;这些异构型网络如果只是需要各自内部通信&#xff0c;那它们只需要实…

【数据结构初阶】四、线性表里的链表(带头+双向+循环 链表)

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 【数据结构初阶】三、 线性表里的链表&#xff08;无头单向非循环链表&#xff09;_高高的胖子的博客-CSDN博客 引言 通过上期对单链表&#xff08;无头单向非循环链表&#xff0…

python+vue理发店管理系统

理发店管理系统主要实现角色有管理员和会员,管理员在后台管理用户表模块、token表模块、收藏表模块、商品分类模块、热卖商品模块、活动公告模块、留言反馈模块、理发师模块、会员卡模块、会员充值模块、会员模块、服务预约模块、服务项目模块、服务类别模块、热卖商品评论表模…

【IC设计】NoC(Network on Chip)调研

文章目录 SoC&#xff08;System on Chip&#xff09;片上系统SoC的概念SoC总线架构存在的问题 互联网络基础①什么是互联网络&#xff1f;②哪里有互联网络&#xff1f;③互联网络的意义&#xff1f;④互联网络的参数有哪些 NoC&#xff08;Network on Chip&#xff09;片上互…

【MySQL基础】--- 约束

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【MySQL学习专栏】&#x1f388; 本专栏旨在分享学习MySQL的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、什么…

近距离看GPU计算-2

文章目录 前言1.SIMT和硬件多线程3.GPU的Memory Hierarchy 前言 本文转自公众号 GPU and Computing 在《近距离看GPU计算》系列第一篇里我们介绍了GPU的一些基础知识及其如何从图形加速设备演化到通用计算平台。本文我们会具体从处理单元设计和存储层次结构两个方面探讨GPU不…

多元化工具汇聚:企业如何提升协同效率?

在现代企业中&#xff0c;协同工作是不可或缺的。然而&#xff0c;随着企业规模的扩大&#xff0c;协同工作的难度也随之增加。针对这些挑战&#xff0c;我们推荐一款多元化工具—J2L3x。在这篇文章中&#xff0c;我们将介绍J2L3x的主要功能和如何利用它来提高企业的协同效率。…

UG\NX二次开发 一个分割曲线的工具

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,里海BlockUI专栏,C\C++-CSDN博客 简介: 今天有群友发了一个工具演示,是一个分割曲线的工具: 我当时想这位好兄弟怎么这么牛逼,原来啊,他跟你们一样喜欢看我的博客。他用我分享的分割曲线的…

go学习-GMP模型

GMP 好理解还是 GPM 好理解&#xff1f; 按照上述图&#xff0c;从上往下&#xff0c;GPM更适合理解 GMP 模型&#xff1a; Go 语言运行时系统中的 Goroutine、用于管理 Goroutine 调度的 Go Scheduler&#xff08;P&#xff09;、机器可用的逻辑处理器数量&#xff08;M&#…

【数据结构复习之路】线性表(严蔚敏版)万字详解主打基础

专栏&#xff1a;数据结构复习之路 数据结构的三要数&#xff1a;逻辑结构、数据的运算、存储结构&#xff08;物理结构&#xff09;。 我接下来要介绍的线性表&#xff0c;顾名思义也将从这三个大方向进行阐述&#xff1a; 一、线性表的定义——逻辑结构 线性表是具有相同…

Flutter绘制拖尾效果

演示&#xff1a; 代码&#xff1a; import dart:ui;import package:flutter/material.dart; import package:kq_flutter_widgets/widgets/chart/ex/extension.dart;class TrailingView extends StatelessWidget {const TrailingView({super.key});overrideWidget build(Build…

成绩发布系统攻略

作为一名教师&#xff0c;管理学生成绩是我们工作中的重要任务之一。传统的手工成绩记录和发布方式已经无法满足现代教育的需求。因此&#xff0c;制作一个高效、安全、便捷的学生成绩发布系统是至关重要的。本文将为您介绍如何制作学生成绩发布系统&#xff0c;以提高教学效率…

MyBatis-Plus的常用注解

一、TableName 在使用MyBatis-Plus实现基本的CRUD时&#xff0c;我们并没有指定要操作的表&#xff0c;只是在Mapper接口继承BaseMapper时&#xff0c;设置了泛型User&#xff0c;而操作的表为user表&#xff0c;由此得出结论&#xff0c;MyBatis-Plus在确定操作的表时&#xf…

Flutter实现PS钢笔工具,实现高精度抠图的效果。

演示&#xff1a; 代码&#xff1a; import dart:ui;import package:flutter/material.dart hide Image; import package:flutter/services.dart; import package:flutter_screenutil/flutter_screenutil.dart; import package:kq_flutter_widgets/widgets/animate/stack.dart…

react如何根据变量渲染组件

三元运算符useMemo 第一种方法的缺点&#xff1a;其他变量更改时&#xff0c;会再次进入三元运算符,例子如下&#xff1a; //这里有一个父组件:Father { n0 ? <Father><div>{111}</div></Father> : <div>{111}</div> }第二种方法如图 …

apk获取MD5方式记录

1&#xff0c;低版本android studio 我这里是Android studio Arctic Fox 直接使用keytool -printcert -jarfile xxx.apk获取 获取得到的效果&#xff1a; 2&#xff0c;高版本android studio 在高版本下&#xff0c;按照如下图点击打开到gradle。在③步骤下直接输入signning…

《向量数据库指南》——火山引擎向量数据库对正式外开放服务

向量数据库技术全景 经过长期的内部探索和优化,抖音采用的向量数据库产品结构如下图所示:基于云基础设施,提供经过深度打磨和优化的各个引擎,提供从多模态数据写入,到向量生成,再到在线检索,以及上线后的弹性调度和监控的一整套全链路解决方案。 火山引擎向量数据库的场…

C++ 里 ++i 是原子操作吗?

1.什么是原子操作 在多线程环境下,原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。 原子操作可以确保某些特定操作在多线程条件下,不会由于线程切换而导致数据污染。比如,对一个变量的读/写…

ASEMI快恢复二极管S1FD40A180H参数,S1FD40A180H应用

编辑-Z S1FD40A180H参数描述&#xff1a; 型号&#xff1a;S1FD40A180H 最大直流反向电压VR&#xff1a;1800V 最大工作峰值反向电压VRWM&#xff1a;1440V 最大平均正向电流IF&#xff1a;40A 非重复正向浪涌电流IFSM&#xff1a;500A 操作和储存温度范围TJ ,TSTG&…

洞察2023:中国心室辅助装置行业竞争格局及市场份额

本文核心数据&#xff1a;代表性企业排名 ; 代表性企业优势分析等 1、中国心室辅助装置行业竞争梯队 人工心脏 ( Artificial Heart, AH ) 是机械辅助类器械的代表&#xff0c;用于替代或辅助心脏泵血功能。按照功能可分为心室辅助装置 ( Ventricular Assist Device&#xff0…