队列的简单实现

news2025/4/3 22:04:37

队列的简单实现

  • 一、什么是队列
  • 二、队列的分类
  • 三、队列的数据结构
  • 四、队列的基本操作
    • 1、初始化队列
    • 2、销毁队列
    • 3、入队
    • 4、出队
    • 5、队列判空
    • 6、获取队头元素
    • 7、获取队尾元素
    • 8、获取队列元素
  • 总结
    • 头文件
    • 基本操作

一、什么是队列

首先我们既然想要实现队列就得明白什么是队列;
队列: 只允许在一端插入数据,在另一端删除数据的特殊线性表;队列具有先进先出的特点;其中插入数据的操作叫做入队列,删除数据的操作叫做出队列
在这里插入图片描述

二、队列的分类

分类:
1、链式结构;
2、顺序结构;

这一点上始于栈是一样的,但是我们知道,栈我们通常使用顺序表实现;而对于队列来说我们通常使用链式结构实现;
why?

就说假设我们利用顺序结构实现队列;顺序表的头作为队头,尾巴作为队尾
那么我们的入队操作是不是就相当于尾插,这一点没问题,效率很高,时间复杂度为O(1),同理我们还有进行出队操作啊,也就是说相当于顺序表的头删,我们通过前面的学习可以知道,顺序表尾插尾删效率很高,但是对于头插、头删效率就表现的不尽人意,因为头删的话需要我们挪动数据,时间复杂度就是O(N),不是很高效;当然如果你想以顺序表的尾作为队列的头的话,那么出队 操作时间复杂度还是O(N),不是很理想;
这也是我们不打算利用顺序表实现队列的一个重要原因!!!
那么为什么要选择利用链式结构?
假设我们是利用单链表的方实现的,我们都知道单链表是不能回走的,那么我们就肯定不会选择单链表的尾作为队列的头,因为这样的话,那么队列的出队操作时间复杂度就是O(N)了,划不来!!!;
为此我们肯定会首选将单链表的尾巴最为队列的尾巴,那吗这时候出队操作时间复杂度不就是O(1);那么这时候就会右小伙伴会问了,那么我入队的操作时间复杂度不就是O(N)嘛,因为我单链表需要找尾才能就行插入,那不和顺序表有啥子区别!!这不就来了,队列的数据结构设计:
我们可以定义一个头指针和尾指针分别来记录队列的头和尾啊,这么一来我不就天然的知道尾指针,就不用去找尾巴了,入队和出队的时间复杂度也是更加方便了,同时空间上也得到了更加充分的利用,但是如果用这样的方法去定义顺序表的话,那么我的头指针尾指针都在变,那么势必会照成我顺序表的起始指针会丢失,那么我到时候释放的时候就找不到整块顺序表的起始指针了,就会照成内存泄漏,这因小失大的操作我们自然不会去选择为此我们才会钟爱于利用链式结构去实现队列;

三、队列的数据结构

上面我们已经阐述了为什么要实现链式结构队列,同时也说明了要想让入队和出队的时间复杂度达到O(1);
就必须定义一个头指针和尾指针来记录队列的队头和队尾,为此我们给出以下数据结构:
在这里插入图片描述
由于我们需要同时记录两个指针的变化,为此我们需要将两个指针定义在结构体里面;
相当于我们只要知道了这两个指针就能操作整个队列;
在这里插入图片描述

四、队列的基本操作

1、初始化队列

队列为空的条件:
frontNULL;//头指针
rear
NULL;//尾指针
为此刚开始时队列一定为空,我们就像这样初始化了:

//初始化队列
void QueueInit(Queue* q)
{
	assert(q);
	q->front = NULL;
	q->rear = NULL;
}

2、销毁队列

初始话和销毁是伴生关系,为此我们将队列的销毁也一同处理了:

//销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)//这里要注意结束条件不是cur==q->rear;如果是这样的话就会照成rear节点没有释放,需要单独处理,很是麻烦,这样代码就变得很挫了;
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = NULL;
	q->rear = NULL;
}

3、入队

刚开始我们的队列是空的,那么rearfrontNewNode//新节点地址;
当我们队列不为空了,那么就需要连接在rear的后面了;
为此这里有两种情况,我们分开处理就好了:


//开辟新节点
static QNode* BuyQNode(QDataType x)
{
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (!NewNode)
	{
		printf("malloc fail!\n");
		exit(EXIT_FAILURE);
	}
	NewNode->next = NULL;
	NewNode->val = x;
	return NewNode;
}
//入队列
void QueuePush(Queue* q,QDataType x)
{
	assert(q);
	QNode* NewNode = BuyQNode(x);
	if (q->rear == NULL)
	{
		q->front = q->rear = NewNode;
	}
	else
	{
		q->rear->next = NewNode;
		q->rear = NewNode;
	}
}

4、出队

入队讲完了,咱们接着讲讲出队,这出队操作啊也就是单链表的头删:
只需要释放点front所指的位置,并且更新front指针就行了,但是这会照成一个问题,要是我一直出队一直出,那么最后肯定会删光对吧:
在这里插入图片描述
正如上面这幅图(只剩最后一个节点),要是我还接着出队列,那么队列的是不是就为空啊:
在这里插入图片描述
最后是不是就会造成这副模样,可是我们有没有发现我们出队的时候一直是改变的头指针唉,像这样删的话,最后我的尾指针会指向一个已经内释放的节点,有野指针的嫌疑,同时我们对于队列判空的条件(front==NULL&&rear==NULL)就不在适用了,这造成的一一系列连锁反应,就会让我们优雅的程序出现致命的错误!!!,为此向上述情况我们需要特殊处理,当front删完一个节点过后我们都要判断一下front等不等于NULL,如果是,那说明刚才删除的是最后一个节点,为了不照成野指针的麻烦,我们需要将rear也置空!!!;如果不是,则说明刚才删除的不是最后一个节点,我们无需对rear进行过多处理;
为此我们的出队代码如下:

//出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(QueueEmpty(q)==false);//判空
	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	if (q->front == NULL)//删除节点过后front指针指向NULL,表示无节点可删
		q->rear = q->front;
}

5、队列判空

//队列判空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return (q->front == q->rear && q->front == NULL);
}

6、获取队头元素

我们知道头指针,只需判断一下队列为不为空(因为我们要有数据可取才行!!!),就可以取数据了;

//获取队头元素
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->val;
}

7、获取队尾元素

这不我们定义尾指针的好处不就来了!!要是我们没有这个尾指针,就得自己找尾,时间复杂度难免会O(N),但是现在简直方便的变态!!!时间复杂度O(1)!!!

//获取队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->rear->val;
}

8、获取队列元素

常见错误:
直接用尾指针减去头指针
注意这不是顺序表,用尾指针减去头指针不是元素个数,是个随机值,指针不是一定是连续的,这里的节点是一个一个开辟的,其地址自然也是随机的,这么多数据的地址不可能全部都是连在一起的,为此尾指针减去头指针的做法是不可取的!!!
那么比较常见的方法:
1、在结构里面里面直接价格成员size,用来记录有效元素个数;\这样求队列元素时间复杂度:O(1);
2、不用定义成员变量,直接遍历整个链表,只不过这个时间复杂度就有点大了,时间复杂度:O(N);
由于我们刚开始的时候没有定义size这个成员变量,我们这里就才有遍历的方法取求解了:

//获取队列元素个数
size_t QueueSize(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	size_t count = 0;
	while (cur)
	{
		QNode* next = cur->next;
		count++;
		cur = next;
	}
	return count;
}

总结

头文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
// 链式结构:表示队列 
typedef int QDataType;
typedef struct QListNode
{
	struct QListNode* next;
	QDataType val;
}QNode;

// 队列的结构 
typedef struct Queue
{
	QNode* front;
	QNode* rear;
}Queue;
 
//初始化队列
void QueueInit(Queue*q);
//销毁队列
void QueueDestroy(Queue*q);
//入队列
void QueuePush(Queue*q,QDataType x);
//出队列
void QueuePop(Queue*q);
//获取队头元素
QDataType QueueFront(Queue*q);
//获取队尾元素
QDataType QueueBack(Queue* q);
//获取队列元素个数
size_t QueueSize(Queue*q);
//队列判空
bool QueueEmpty(Queue*q);

基本操作

#include"Queue.h"

//初始化队列
void QueueInit(Queue* q)
{
	assert(q);
	q->front = NULL;
	q->rear = NULL;
}
//销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	q->front = NULL;
	q->rear = NULL;
}

//开辟新节点
static QNode* BuyQNode(QDataType x)
{
	QNode* NewNode = (QNode*)malloc(sizeof(QNode));
	if (!NewNode)
	{
		printf("malloc fail!\n");
		exit(EXIT_FAILURE);
	}
	NewNode->next = NULL;
	NewNode->val = x;
	return NewNode;
}
//入队列
void QueuePush(Queue* q,QDataType x)
{
	assert(q);
	QNode* NewNode = BuyQNode(x);
	if (q->rear == NULL)
	{
		q->front = q->rear = NewNode;
	}
	else
	{
		q->rear->next = NewNode;
		q->rear = NewNode;
	}
}
//出队列
void QueuePop(Queue* q)
{
	assert(q);
	assert(QueueEmpty(q)==false);//判空
	QNode* next = q->front->next;
	free(q->front);
	q->front = next;
	if (q->front == NULL)//删除节点过后front指针指向NULL,表示无节点可删
		q->rear = q->front;
}
//队列判空
bool QueueEmpty(Queue* q)
{
	assert(q);
	return (q->front == q->rear && q->front == NULL);
}
//获取队头元素
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->front->val;
}
//获取队尾元素
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(!QueueEmpty(q));
	return q->rear->val;
}
//获取队列元素个数
size_t QueueSize(Queue* q)
{
	assert(q);
	QNode* cur = q->front;
	size_t count = 0;
	while (cur)
	{
		QNode* next = cur->next;
		count++;
		cur = next;
	}
	return count;
}

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

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

相关文章

1.7.4、计算机网络体系结构中的术语

1.7.4、计算机网络体系结构中的术语 1.7.4.1、实体 实体&#xff1a; 任何可发送或接收信息的硬件或软件进程。 对等实体&#xff1a; 收发双方相同层次中的实体 1.7.4.2、协议 协议&#xff1a;控制两个的对等实体进行逻辑通信的规则的集合 之所以称为逻辑通信&#xf…

目标检测论文解读复现之五:改进YOLOv5的SAR图像舰船目标检测

目标检测论文解读复现 文章目录目标检测论文解读复现前言一、摘要二、网络模型及核心创新点三、应用数据集四、实验效果&#xff08;部分展示&#xff09;五、实验结论六、投稿期刊介绍前言 此前出了目标改进算法专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改…

HTML5期末考核大作业,电影网站——橙色国外电影 web期末作业设计网页

HTML实例网页代码, 本实例适合于初学HTML的同学。该实例里面有设置了css的样式设置&#xff0c;有div的样式格局&#xff0c;这个实例比较全面&#xff0c;有助于同学的学习,本文将介绍如何通过从头开始设计个人网站并将其转换为代码的过程来实践设计。 文章目录一、网页介绍一…

【代码精读】ATF的异常向量表

快速链接: . 👉👉👉 【代码精读】–Kernel/ATF/optee等-目录👈👈👈 付费专栏-付费课程 【购买须知】:本专栏的视频介绍-----视频👈👈👈概要: 本文概述了ARMv8/ARMv9的aarch64体系中异常向量表的结构、以及基地寄存器的总结。然后通过导读ATF BL31的异常向量…

Flink系列文档-(YY09)-Flink时间语义

1 三种时间语义 在实时流式计算中&#xff0c;"时间"是一个能影响计算结果的非常重要因素&#xff01; 试想场景&#xff1a;每隔1分钟计算一次最近10分钟的活跃用户量&#xff1a; ①假设此刻的时间是13:10&#xff0c;要计算的活跃用户量时间段为&#xff1a;[ …

【C++】类和对象(下)

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;再谈构造…

kindle自定义屏保之自定义字帖

kindle自定义屏保之自定义字帖 01 前言 毕业以后&#xff0c;很少动笔写字了&#xff0c;某天要手写一堆材料&#xff0c;写出来实在不忍直视&#xff0c;于是当晚下班后突发奇想——能不能把一些字帖搞成kindle屏保&#xff0c;摆在桌面上&#xff0c;睡前说不准还能练练 随…

web课程设计 基于html+css+javascript+jquery女性化妆品商城

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

软件测试基础知识

软件测试基础知识1.测试模型2.测试分类3.测试目的与原则4.测试流程5.测试发展规划6.单元测试7.黑盒测试8.白盒测试9.缺陷1.测试模型 瀑布模型 开发将系统都做好了&#xff0c;然后测试。最大问题是测试工作后置&#xff0c;导致整个项目开发完成之后如果发现比较重要的问题&…

基于微信小程序的校运会管理系统设计与实现-计算机毕业设计源码+LW文档

小程序开发说明 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Mave…

【大学课程设计】计算器实现(附源码)

&#x1f388; 作者&#xff1a;Linux猿 &#x1f388; 简介&#xff1a;CSDN博客专家&#x1f3c6;&#xff0c;华为云享专家&#x1f3c6;&#xff0c;Linux、C/C、云计算、物联网、面试、刷题、算法尽管咨询我&#xff0c;关注我&#xff0c;有问题私聊&#xff01; &…

6.Paddle Graph Learning (PGL)图学习之图游走类模型[系列四]

Paddle Graph Learning (PGL)图学习之图游走类模型[系列四] 更多详情参考&#xff1a;Paddle Graph Learning 图学习之图游走类模型[系列四] https://aistudio.baidu.com/aistudio/projectdetail/5002782?contributionType1 相关项目参考&#xff1a; 关于图计算&图学习…

路由规划——运输距离的估算

运输距离的估算1. 常规的拟合距离1.1 欧氏距离1. 2 球面距离拟合2. 一种改进的球面距离拟合参考文献在进行路径规划时&#xff0c;需要获取点与点之间的距离&#xff0c;点之间的距离通常是通过坐标或者经纬度计算得到&#xff0c;可分为拟合距离和导航距离两类。导航距离顾名思…

基础二叉树及其高频面试题

目录 一、树的概念及其结构 1.1 树的概念 1.2 树的相关概念 1.3 树的表示法 二、二叉树的概念及其结构 2.1 概念 2.2 特殊二叉树 2.3 二叉树的性质 2.4 二叉树的存储结构 顺序存储 链式存储 三、链式二叉树 3.1 遍历方式 深度优先遍历:DFS 层序遍历:BFS(广度优先…

【附源码】Python计算机毕业设计社区防疫信息管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Debian11中 Nginx1.22.1 php8.1.12 Mariadb10.5.15的安装

起因&#xff1a;我之前发的关于LEMP的搭建的文章&#xff0c;已经有3年9个月了&#xff0c;各个软件的版本更新了比较多。最主要的是&#xff0c;CentOS系统终止了&#xff0c;我也完全更换到了Debian系统之上。这里重新搭建了一下&#xff0c;主要是计划结合frp&#xff0c;构…

Vue | Vue.js 全家桶 Pinia状态管理

&#x1f5a5;️ Vue .js专栏&#xff1a;Node.js Vue.js 全家桶 Pinia状态管理 &#x1f9d1;‍&#x1f4bc; 个人简介&#xff1a;一个不甘平庸的平凡人&#x1f36c; ✨ 个人主页&#xff1a;CoderHing的个人主页 &#x1f340; 格言: ☀️ 路漫漫其修远兮,吾将上下而求索☀…

[基础服务] [操作系统] 类Linux的文件和目录

&#x1f341;简介 在奔腾70年代的中美建交之际,UNIX 也逐渐展露头角(也有说是60年代末),十五年后Windows诞生了,又过了五年Linux横空出世三大主流操作系统直到现在呈现三足鼎立之势~ 出生时间是&#xff1a; UNIX(70年代初) > Windows(80年代中) > Linux (90年代初) 可以…

基于微信小程序的电影院票务系统设计与实现-计算机毕业设计源码+LW文档

小程序开发说明 开发语言&#xff1a;Java 框架&#xff1a;ssm JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09; 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Mav…

HCIP-Datacom OSPF进阶(二)最常用的路由协议 OSPF各种LSA作用详解

目录 OSPF路由计算&#xff1a; LSA头部信息&#xff1a; Router-LSA&#xff08;1类&#xff09;&#xff1a; 一类LSA&#xff1a; 一类LSA可以描述四种链路类型&#xff1a; Network-LSA&#xff08;2类&#xff09;&#xff1a; 二类&#xff1a; IR、ABR、ASBR是什…