《数据结构与算法之美》学习笔记五之队列

前情提要:上一章学习了栈相关的知识,主要有下面的内容:
  • 栈操作的时间复杂度,对于顺序栈,入栈时如果栈的空间不够涉及到数据搬移,此时使用摊还分析法,将数据搬移的耗时均摊到不需要搬移数据的入栈操作中,均摊时间复杂度等于最好情况时间复杂度 O(1)
  • 栈在函数调用中的应用,内存给每一个线程都分配了一块独立的内存空间,这些内存空间被组织成“栈”的形式,用来存储函数调用时的临时变量,当进入一个函数时,会将这个函数作为一个栈帧入栈,当函数执行完毕时,会将对应的栈帧出栈。
  • 栈在表达式中的应用,对于加减乘除等等数学表达式的运算,计算机理解起来是很困难的,需要使用栈来辅助。需要一个运算符栈和一个操作数栈,遍历表达式,当遇到操作数,就压入操作数栈,当遇到运算符,则需要和运算符栈的栈顶运算符比较优先级,如果栈顶元素优先级高,如果当前运算符优先级更高,就压入运算运算符栈,继续下次对比;
  • 如何使用栈实现浏览器的前进后退功能?和计算表达式的值有点类似,也是需要两个栈,当访问新页面的时候,把页面压入栈A;当点击后退时,取出栈A的栈顶元素,压入栈B;当点击前进时,从栈B中取出栈顶元素,压入栈A;如果要访问新页面,就需要清除栈B

这一章继续来学习队列
队列比栈复杂那么一丢丢

(一)队列基本概念

队列的基本概念很好理解,就类似于买票排队,先来的先买,后来的排到队尾,也就是咱们经常听到的“先进先出”
队列与栈类似,也只支持两种操作:“入队” 和 “出队”。“入队” 就是新增一个元素到队列的末尾,“出队” 就是从队列的头部取出一个元素。
在这里插入图片描述
所以队列和栈一样,是一种操作受限的线性表数据结构。

(二)顺序队列和链式队列

跟栈一样,队列也可以用数组和链表实现。用数组实现的叫顺序队列,用链表实现的叫链式队列
下面是使用 Java 语言写的队列的数组实现

// 用数组实现的队列
public class ArrayQueue {// 数组:items,数组大小:nprivate String[] items;private int n = 0;// head表示队头下标,tail表示队尾下标private int head = 0;private int tail = 0;// 申请一个大小为capacity的数组public ArrayQueue(int capacity) {items = new String[capacity];n = capacity;}// 入队public boolean enqueue(String item) {// 如果tail == n 表示队列已经满了if (tail == n) return false;items[tail] = item;++tail;return true;}// 出队public String dequeue() {// 如果head == tail 表示队列为空if (head == tail) return null;// 为了让其他语言的同学看的更加明确,把--操作放到单独一行来写了String ret = items[head];++head;return ret;}
}

队列的数组实现比栈的数组实现复杂了那么一丢丢。
对于栈来说,只需要一个栈顶指针,因为入栈出栈都是在栈顶进行操作。而队列需要一个队头指针 head 和一个队尾指针 tail 。可以结合下面这张图来理解
在这里插入图片描述
当 a b c d 依次入队之后,队列的 head 指针就指向索引为0的位置,tail 指针就指向索引为4的位置。
当我们调用了两次出队操作之后,head指针往后移动两位,指向索引为2的位置,tail指针还是指向索引为4的位置
在这里插入图片描述
随着不停地入队、出队操作,head 指针和 tail 指针都会往后移动,如果这两个指针重合,即使数组中还有位置,也没办法再进行入队操作了。
回想一下数组那一章节,删除一个元素,造成数组空间不连续,后续再往数组中增加元素的时候,这个空出来的空间是没有办法利用的。此时我们怎么解决数据不连续的问题呢?对,就是使用数据搬移
每次的出队操作都相当于删除了索引为0的元素。但是我们并不需要在每次出队的时候都进行数据搬移,这样子会导致出队的时间复杂度变为 O(n),只需要在入队时,发现没有空闲空间的时候,进行数据搬移。
这样子的话,队列的出队函数并不需要修改,入队函数需要修改一下下

// 入队操作,将item放入队尾
public boolean enqueue(String item) {// tail == n表示队列末尾没有空间了if (tail == n) {// tail ==n && head==0,表示整个队列都占满了if (head == 0) return false;// 数据搬移for (int i = head; i < tail; ++i) {items[i-head] = items[i];}// 搬移完之后重新更新head和tailtail -= head;head = 0;}items[tail] = item;++tail;return true;
}

从代码中可以看到,当队列的 tail 指针移动到最右边后,再往队列中添加元素时,就会将 head 指针到 tail 指针之间的数据往前搬移,从索引为0开始,到 tail - head 结束
在这里插入图片描述
在这种实现思路下,出队操作的时间复杂度仍然是 O(1)。入队操作,我们之前在数组那一章节分析过,均摊时间复杂度就等于最好情况时间复杂度,也是 O(1)。
接下来我们再来看一下基于链表的队列实现方式。
基于链表的实现,我们同样需要两个指针,head 指针指向链表的第一个结点,tail 指针指向链表的最后一个结点。如下图所示,
入队时,tail.next = newNode;tail = tail.next
出队时,head = head.next

在这里插入图片描述

(三)循环队列

循环队列,顾名思义,就是原来的拉直的队列首尾相连成一个圈圈
在这里插入图片描述
我们可以发现,图中的队列大小为8,head指针指向 4 的位置,tail 指针指向 7 的位置。
当有一个新元素入队的时候,新元素会放在 7 的位置,tail 指针并不需要更新为 8 ,而是需要到 0 的位置,同样的,再次新增时,tail 指针继续往前移动,到 1 的位置。新增两个元素的状态如下图所示:
在这里插入图片描述
通过这种方式,在队列空间满之前,都不需要进行数据搬移操作。
但是循环队列的实现,相比于非循环队列,会复杂一些。关键在于两个状态的确定,一个是队列为空的状态,一个是队列满员的状态。
在非循环的队列中,队列为空的判断标准是 head == tail ,队列满员的判断标准是 tail == n
在循环队列中,队列为空的判断条件依然是 head == tail,队列满员的状态如下图所示
在这里插入图片描述
队列满员的判断条件,可以通过多🖼几次图总结出来,是 (tail+1)%n=head。当队列满的时候,tail 的位置是没有存储数据的,这会造成一丢丢内存的浪费。

public class CircularQueue {// 数组:items,数组大小:nprivate String[] items;private int n = 0;// head表示队头下标,tail表示队尾下标private int head = 0;private int tail = 0;// 申请一个大小为capacity的数组public CircularQueue(int capacity) {items = new String[capacity];n = capacity;}// 入队public boolean enqueue(String item) {// 队列满了if ((tail + 1) % n == head) return false;items[tail] = item;tail = (tail + 1) % n;return true;}// 出队public String dequeue() {// 如果head == tail 表示队列为空if (head == tail) return null;// 取出头部String ret = items[head];// 更新head到前一个位置head = (head + 1) % n;return ret;}
}

(四)阻塞队列

阻塞队列其实就是在队列的基础上增加了阻塞操作。当队列为空的时候,从对头取数据的操作就会被阻塞,等到队列中有数据的时候,在从队头取出数据并返回;入队操作也一样,当队列满的时候,入队操作会被阻塞,等到队列中有空位的时候才会执行插入操作,并返回。
在这里插入图片描述

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

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

相关文章

django开发流程1

一、官方网站&#xff1a; Django documentation | Django documentation | Djangohttps://docs.djangoproject.com/en/5.1/ 1.安装 django : pip install django 2. django项目的配置文件 (settings.py) BASE_DIR 项目根路径 DEBUG 调试模式 INSTALLE…

DC00018基于java swing+MySQL花卉信息管理系统

1、项目功能演示 DC00018基于java swingMySQL花卉信息管理系统java项目信息管理系统 2、项目功能描述 基于java swingMySQL花卉信息管理系统 系统包括用户信息管理以及花卉信息管理等功能。 3、项目运行截图 4、项目核心代码 4.1 日期格式化 package utils;import java.t…

二进制文件与文本文件的区别【字符集Charset】

计算机上存储的文件在比特位上都是以二进制数字0或1表示&#xff0c;因此在物理层面上&#xff0c;文本文件和二进制文件没有本质差异&#xff0c;都是由数字0或1组成的比特位集合。 文本文件和二进制文件&#xff0c;两者的差异体现在编码逻辑&#xff0c;需要根据文件头中标…

线程中的条件变量pthread_cond_t

条件变量不是锁&#xff0c;但通常结合锁使用&#xff0c;条件变量用于检查某个条件是否满足。 条件变量基本函数 int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);// 动态初始化条件变量&#xff0c;参数cond&#xff1a;条件变量…

Excel怎么自动排序?4种方法任君选择

大家好&#xff0c;这里是效率办公指南&#xff01; &#x1f522; 在处理大量数据时&#xff0c;保持数据的有序性是非常重要的。Excel提供了几种自动排序的方法&#xff0c;可以帮助我们快速地对数据进行排序&#xff0c;确保数据的组织和分析更加高效。今天&#xff0c;我们…

推荐几个前端组件库,真好用!

今天给大家推荐几款的后台管理系统开箱即用的组件库&#xff0c;基于ElementUI二次封装&#xff0c;开发必备 Headless UI Headless UI 是一款出色的前端组件库&#xff0c;专为与 Tailwind CSS 集成而设计。一组完全无样式、完全可访问的 UI 组件&#xff0c;可以自由的引入…

2024网站建设哪家公司比较好TOP3

在数字化时代&#xff0c;随着个人和商业活动越来越多地转移到线上&#xff0c;网站安全性的问题显得尤为重要。用户数据的保护是建立消费者信任和维护企业声誉的基石。靠谱的网站建设供应商深知这一点&#xff0c;他们把网站安全性作为开发过程中的首要考虑因素之一。 首先&a…

数据结构基础之《(5)—链表》

一、单向链表 1、单向链表节点结构&#xff08;可以实现成泛型&#xff09; public class Node{public int value;public Node next;public Node(int data) {value data;} } 2、双向链表节点结构 public class DoubleNode {public int value;public DoubleNode last;publi…

【Golang】Go语言中type关键字到底是什么?

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

FMEA实战指南:精准定参,筑牢产品质量防线

在FMEA分析中&#xff0c;严重度、频度和探测度三个参数的确定直接关系到风险顺序数(RPN)的计算&#xff0c;进而影响产品故障模式的优先排序和改进措施的制定。因此&#xff0c;掌握如何精准确定这些参数&#xff0c;对于提高产品质量、降低风险具有重要意义。深圳天行健企业管…

水面巡检船垃圾漂浮物检测系统源码分享

水面巡检船垃圾漂浮物检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of …

​初学者的自动化测试路线图:Playwright和TypeScript

测试对于确保软件运行良好非常重要。测试自动化通过使用特殊的工具和程序快速准确地进行测试使这变得更容易。这有助于检查软件是否完成了它应该做的事情、它的性能如何以及它是否可靠。 通过自动化重复测试任务&#xff0c;团队可以显着加快测试过程&#xff0c;扩大测试覆盖…

泛微OA提示信息换行

⭐️如果对你有用的话&#xff0c;希望可以点点赞&#xff0c;感谢了⭐️ WfForm.setTextFieldEmptyShowContent("field111", "格式模板&#xff1a;将顾客客诉原因文字描述清楚\n如&#xff1a;顾客因对美养师手法不满&#xff0c;觉得力度不够&#xff0c;没…

架构师:消息队列的技术指南

1、简述 消息队列(Message Queue, MQ)是一种异步通信机制,允许系统的各个组件通过消息在彼此之间进行通信。消息队列通过解耦系统组件、缓冲高峰期请求和提高系统的可扩展性,成为分布式系统中不可或缺的一部分。 2、工作原理 消息队列的基本工作原理是生产者将消息发布到…

远程办公生产力软件推荐,每天比同事早下班3个小时的秘密!

每天比同事早下班3个小时的秘密&#xff0c;终于被我找到啦&#xff01; 网易GameViewer远程是一款高效便捷的远程办公工具&#xff0c;支持多点触控、虚拟鼠标键盘、4K画质和低延迟。其隐私屏功能保护文件安全。 只需三步&#xff1a;安装、登录、远控&#xff0c;即可轻松提升…

Docker-2.如何保存数据退出

在使用Docker时&#xff0c;我们常常需要修改容器中的文件&#xff0c;并且希望在容器重启后这些修改能够得到保留。 0.简介 使用Docker时有一个需要注意的问题&#xff1a;当你修改了容器中的文件后&#xff0c;重启容器后这些修改将会被重置&#xff0c;深入研究这个问题。 …

远程访问软路由

远程访问软路由主要涉及通过互联网从远程位置访问和控制基于软件的路由器系统。以下是远程访问软路由的一般方法&#xff1a; 一、远程访问软路由的方法 通过Web管理界面访问&#xff1a; 适用于大多数支持Web管理的软路由系统。用户只需在浏览器中输入软路由的公网IP地址或域…

react中的ref三种形式

1&#xff0c;字符串形式 <!-- 创建盒子 --><div id"test"></div> <script type"text/babel">class Demo extends React.Component{render(){return(<div><input type"text" refinput1 /><button onCl…

从销售到 AI 算法工程师 | 转行人工智能大模型(含面经裁员幸存指南)

我叫王东&#xff0c;90后&#xff0c;和大家分享一下我的人工智能转型之路。 农学毕业&#xff0c;投身互联网做销售 机遇难求&#xff0c;养殖梦碎 我是土生土长的农村人&#xff0c;小时候经常和小鱼小虾打交道&#xff0c;上大学的时候就选择了农学专业&#xff0c;想着…

OpenKylin--解压文件

tar zxf dotnet-sdk-6.0.425-linux-x64.tar.gzrootsanzk-pc:/home/dotnet# tar zxf dotnet-sdk-6.0.425-linux-x64.tar.gz参考&#xff1a; rootxxx-pc:/home/xxx# mkdir -p /home/dotnet && tar zxf dotnet-sdk-6.0.411-linux-x64.tar.gz -C /home/dotnet mkdir -p /…