当前位置: 首页 > news >正文

JavaDS —— 栈 Stack 和 队列 Queue

栈的概念

栈是一种先进后出的线性表,只允许在固定的一端进行插入和删除操作。
进行插入和删除操作的一端被称为栈顶,另一端被称为栈底

栈的插入操作叫做进栈/压栈/入栈
栈的删除操作叫做出栈

在这里插入图片描述
现实生活中栈的例子:
在这里插入图片描述

栈的模拟实现

下面是Java集合类给我们提供的栈的方法:

在这里插入图片描述

模拟实现顺序栈

我们通过数组来模拟实现栈。

构建数组栈

我们需要两个成员变量,一个是数组,另一个是记录当前的数据个数。

    public int[] elem;public int usedSize;public MyStack() {elem = new int[5];}

push

要考虑扩容问题

    private boolean isFull() {if(usedSize == elem.length) {return true;}return false;}private void grow() {elem = Arrays.copyOf(elem,2*elem.length);}public void push(int val) {if(isFull()) {grow();}elem[usedSize++] = val;}

isEmpty

判断栈是否为空:

    public boolean isEmpty() {return usedSize == 0;}

pop

设置自定义异常:

public class PopException extends RuntimeException{public PopException() {super();}public PopException(String message) {super(message);}
}

删除栈顶的同时,还会返回删除的元素

    private void checkPop() throws PopException{if(isEmpty()) {//抛异常throw new PopException("栈已空,无法删除!!!");}}public int pop() {try {checkPop();int ret = elem[usedSize-1];usedSize--;return ret;} catch (PopException e) {e.printStackTrace();}return -1;}

peek

获取栈顶元素 但是不删除

    public int peek() {try {checkPop();return elem[usedSize-1];} catch (PopException e) {e.printStackTrace();}return -1;}

链式栈

链式栈顾名思义就是利用链表来保存栈

假设使用单链表制作链式栈,建议使用头插和头删法来进行push和pop操作,peak直接把头节点的值获取即可,这样时间复杂度可以为O(1);但是如果你使用尾插和尾删就是以尾节点的位置作为栈顶,在push,pop 和 peak 的时候,时间复杂度为O(N)

假设使用双向无头循环链表,无论是选择头插头删还是尾插尾删作为push与pop,时间复杂度都是O(1)

这里就不演示链式栈的代码。

Stack 的使用

在这里插入图片描述
push 入栈;pop 出栈;peak 获取栈顶元素;empty 是否为空;size 这个获取有效元素的方法是在Vector 中的,search 找到某元素距离栈顶元素的距离多少(栈顶元素记为1,然后一直到目标元素)

栈、虚拟机栈、栈帧有什么区别呢?

栈是我们的数据结构的其中一种形式,虚拟机栈是JVM虚拟机的一块内存,栈帧是运行一个方法或者函数的时候计算机给它开辟的内存。

队列的概念

队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out) 入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头(Head/Front)

队列类似我们生活中的排队。

在这里插入图片描述

队列的模拟实现

在这里插入图片描述
上面是Java集合类给我们提供的队列的方法,其中add和offer都是入队操作,poll 和 remove 都是出队操作,element 和 peek 都是获取队列头部的元素。

它们主要的区别是会不会抛异常~~ 下面有详细的介绍:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


链式队列

这里我们将使用数组来模拟实现队列,并且这里只实现下图所示的方法:
在这里插入图片描述

size 和 isEmpty 是队列继承的方法,并且队列没有重写这两个方法,所以上面的IDEA直接看队列的Structure 是看不到的。


假设我们使用单链表为基础,该怎么实现队列?
假设入队采用尾插,那要出队即使用头删即可
出队列使用单链表的头删即可,时间复杂度为O(1)
入队列使用尾插,一般情况下,单链表实现尾插首先要找到尾,然后才是开始插入,这时候时间复杂度就尾O(N),不是最优解,我们可以加一个尾指针保存好尾节点,这样就方便我们快速进行尾插操作。

假设入队使用头插,那出队的时候就需要使用尾删
这时候就不好弄了,即使你有尾指针在进行尾删的时候还是需要遍历链表找到尾节点的前一个结点才能完成尾删,这时候你可能会认为再定义一个指针,这就很麻烦了。

所以单链表构建队列的话,限制条件有点大,但是使用上一片文章的双向链表(无头双向循环链表 LinkedList) ,就可以随意选取一端作为入队,另一端作为出队。

这里我们入队采用尾插,出队采用头删:

public class MyQueue {public static class ListNode {public int val;public ListNode prev;public ListNode next;public ListNode(int val) {this.val = val;}}public ListNode head;public ListNode last;//入队public boolean offer(int data) {ListNode node = new ListNode(data);if(isEmpty()) {last = head = node;} else {last.next = node;node.prev = last;last = node;}return true;}public boolean isEmpty() {return head == null;}//出队public int poll() {if(isEmpty()) {return -1;}int ret = head.val;if(head.next != null) {head.next.prev = null;}head = head.next;return ret;}//求结点个数public int size() {ListNode cur = head;int count = 0;while(cur != null) {count++;cur = cur.next;}return count;}//获取队头元素public int peek() {if(isEmpty()) {return -1;}return head.val;}
}

这里要注意出队的代码,如果只有一个结点的情况下,你进行删除后就没有结点了,head.next.prev = null 这行代码就会发生报错。

顺序队列

顺序队列我们采用数组来实现队列,这时候我们就要思考一个问题,在不断的出队入队的情况下怎么保证空间尽可能地利用起来?
假设数组的数据已满装满的情况下,我们一直出队直到数组变空的话,怎么利用起来前面的空间?

在这里插入图片描述

循环队列

这时候我们就需要使用循环队列让队列的空间有效的利用起来。

法1 :定义一个成员变量,usedSize 记录使用了多少的空间
法2 : 定义一个标记符,记录空间是否已满
法3 :浪费一个空间,通过数学公式判断队列是否已满

在这里插入图片描述
一般情况下,我们会认为这就是队列空和满的两种状态,但是这两种状态都是 front == near,怎么办?
根据法1,我们可以记录使用了多少空间的usedSize 来判断队列是否已满,即 usedSize = 数组的长度即可认为队列已满
根据法2,我们使用标记符,首先 将标记符记为 -1,认为队列没满,当front 与near 再次相遇时,标记符乘 -1 变为1 ,判断 队列 已满,如果下一个操作时出队,那标记符再自乘 -1变回 -1 ,当front 与 rear 再次相遇时标记符自乘 -1 变为1 表示队列已满,以此类推,这里大家可以自由发挥。


第三个方法是利用队列自身来进行判断,当 rear 的下一个就是 front 的时候(即 ( rear + 1 ) % 数组长度 == front),就判断队列已满,这需要我们浪费队列的一个空间。
在这里插入图片描述


如何让rear 和 front 循环起来呢?即rear 的下标该如何制定呢?
这里有一个公式,当 rear 要 自增的时候,新的 rear = ( rear + 1 ) % 数组长度就是此时rear 对应的实际下标,当 rear 为 7 时,rear 向下移动一格时,新的 rear 就是 ( 7 + 1 ) % 8 = 0, 正好就是 7 下一个的下标值 0


问题拓展 (数组下标循环的小技巧)

下标往后移动(offset 小于 array.length): index = (index + offset) % array.length

在这里插入图片描述


下标往前移动(offset 小于 array.length): index = (index + array.length - offset) % array.length

array.length - offset 其实就是变相地让 小标变成向后 移动。
在这里插入图片描述


顺序队列的代码
public class MyQueue {int front;int rear;int[] elem;public MyQueue() {elem = new int[4];}//入队public boolean offer(int data) {if(isFull()) {return false;}elem[rear] = data;rear = (rear + 1) % elem.length;return true;}//队列是否已满private boolean isFull() {return (rear + 1) % elem.length == front;}//队列是否为空public boolean isEmpty() {return rear == front;}//出队public int poll() {if(isEmpty()) {return -1;}int ret = elem[front];front = (front + 1) % elem.length;return ret;}//求元素个数public int size() {int count = 0;for (int i = front; i < rear; i++) {count++;}return count;}//获取队头元素public int peek() {if(isEmpty()) {return -1;}return elem[front];}
}

Queue

在这里插入图片描述
上面是我们常用的队列方法

在这里插入图片描述
队列Queue本质上是一个接口,所以Queue 不能被实例化,那如何使用?

Deque (双端队列)

双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。

在这里插入图片描述
在这里插入图片描述
我们可以发现 Deque 其实是继承了 Queue 接口,但是还是一个接口,还是不能实例化,那怎么使用?请看下面解说。

LinkedList 和栈与队列的关系

在这里插入图片描述

在这里插入图片描述

LinkedList 有很多接口其中包括了 Deque ,而Deque 这个接口其实继承了 Queue ,也就是队列,所以我们可以实例化 一个LinkedList 对象然后通过 Queue 接收就可以使用普通队列的方法,同理,通过实例化一个LinkedList 对象然后通过 Deque 接收就可以使用双端队列的方法


    public static void main(String[] args) {LinkedList<Integer> linkedList = new LinkedList<>();linkedList.push(1);linkedList.push(2);linkedList.push(3);System.out.println(linkedList.peek());System.out.println(linkedList.pop());System.out.println(linkedList.peek());}

在这里插入图片描述

LinkedList还可以当成栈来使用,也就是说LinkedList还包含栈的方法,自身功能很强大。

小结:
LinkedList 不仅可以作为不带头的双向循环链表使用,还可以当成栈或者队列使用。


在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口。

Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现
Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现

习题链接:
http://t.csdnimg.cn/aV91m

相关文章:

JavaDS —— 栈 Stack 和 队列 Queue

栈的概念 栈是一种先进后出的线性表&#xff0c;只允许在固定的一端进行插入和删除操作。 进行插入和删除操作的一端被称为栈顶&#xff0c;另一端被称为栈底 栈的插入操作叫做进栈/压栈/入栈 栈的删除操作叫做出栈 现实生活中栈的例子&#xff1a; 栈的模拟实现 下面是Jav…...

C++进阶:继承和多态

文章目录 ❤️继承&#x1fa77;继承与友元&#x1f9e1;继承和静态成员&#x1f49b;菱形继承及菱形虚拟继承&#x1f49a;继承和组合 ❤️多态&#x1fa77;什么是多态&#xff1f;&#x1f9e1;多态的定义以及实现&#x1f49b;虚函数&#x1f49a;虚函数的重写&#x1f499…...

【八大排序】java版(上)(冒泡、快排、堆排、选择排序)

文章目录 一、冒泡排序(重点)思路代码 二、快排(面试重点)思路代码 三、堆排序(面试重点)思路代码 四、选择排序思路代码 一、冒泡排序(重点) 思路 前后两两数据进行比较&#xff0c;小的数据往前走&#xff0c;大的数据往后走&#xff0c;每一轮结束之后&#xff0c;最大的数…...

.Net Core 微服务之Consul(二)-集群搭建

引言: 集合上一期.Net Core 微服务之Consul(一)(.Net Core 微服务之Consul(一)-CSDN博客) 。 目录 一、 Consul集群搭建 1. 高可用 1.1 高可用性概念 1.2 高可用集群的基本原理 1.3 高可用集群的架构设计 1.3.1 主从复制架构 1.3.2 共享存储架构 1.3.3 负载均衡…...

C++ --> 类和对象(二)

前言 在前面简单的介绍了OOP&#xff0c;什么是类&#xff0c;在类中的this指针。接下来就深入理解类和对象。 默认成员函数 默认构造函数&#xff1a;用于在创建对象时初始化对象的成员变量。默认拷贝构造函数&#xff1a;用于使用已存在的对象来初始化新创建的对象。默认析构…...

利用宝塔安装一套linux开发环境

更新yum&#xff0c;并且更换阿里镜像源 删除yum文件 cd /etc/yum.repos.d/ 进入yum核心目录 ls sun.repo rm -rf * 删除之前配置的本地源 ls 配置阿里镜像源 wget -O /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo 配置扩展包 wge…...

VB 实例:掌握 Visual Basic 编程的精髓

VB 实例:掌握 Visual Basic 编程的精髓 引言 Visual Basic(简称VB)是一种由微软开发的高级编程语言,它结合了易于使用的界面和强大的编程功能,使得初学者和专业人士都能快速开发Windows桌面应用程序。本文将通过一系列实例,深入探讨VB编程的基础知识和高级技巧,帮助读…...

层次分析法:matlab代码实现

计算权重&#xff1a; 一、算术平均法 关于矩阵&#xff1a; 1、矩阵的输入写法 [ ; ; ]同行用空格或逗号隔开&#xff0c;不同行用分号间隔 2、矩阵求和 默认按列求和 asum(E) 等同于 asum(E,1) 得到行向量 按行求和 asum(E,2) 得到列向量 对整个矩阵求和 asum(E,"all&…...

07-7.5.3 处理冲突的方法

&#x1f44b; Hi, I’m Beast Cheng &#x1f440; I’m interested in photography, hiking, landscape… &#x1f331; I’m currently learning python, javascript, kotlin… &#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…...

几何距离与函数距离:解锁数据空间中的奥秘

几何距离&#xff1a;直观的空间度量 几何距离&#xff0c;顾名思义&#xff0c;是我们在几何学中熟悉的距离概念&#xff0c;如欧几里得距离、曼哈顿距离和切比雪夫距离等。这些距离度量直接反映了数据点在多维空间中的位置关系。 欧几里得距离&#xff1a;最为人熟知的几何距…...

LabVIEW的Actor Framework (AF) 结构介绍

LabVIEW的Actor Framework (AF) 是一种高级架构&#xff0c;用于开发并发、可扩展和模块化的应用程序。通过面向对象编程&#xff08;OOP&#xff09;和消息传递机制&#xff0c;AF结构实现了高效的任务管理和数据处理。其主要特点包括并发执行、动态可扩展性和强大的错误处理能…...

gitlab 搭建使用

1. 硬件要求 ##CPU 4 核心500用户 8 核心1000用户 ##内存 4 G内存500用户 8 G内存1000用户 2. 下载 链接 3. 安装依赖 yum -y install curl openssh-server postfix wget 4. 安装gitlab组件 yum -y localinstall gitlab-ce-15.9.3-ce.0.el7.x86_64.rpm 5. 修改配置文…...

探索JT808协议在车辆远程视频监控系统中的应用

一、部标JT808协议概述 随着物联网技术的迅猛发展&#xff0c;智能交通系统&#xff08;ITS&#xff09;已成为现代交通领域的重要组成部分。其中&#xff0c;车辆远程监控与管理技术作为ITS的核心技术之一&#xff0c;对于提升交通管理效率、保障道路安全具有重要意义。 JT8…...

视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机,包括T80005系列高清HDMI编码器、4K超高清HDMI编码器

视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机&#xff0c;包括T80005系列高清HDMI编码器、4K超高清HDMI编码器。 视频使用操作说明书-T80005系列视频编码器如何对接海康NVR硬盘录像机&#xff0c;包括T80005系列高清HDMI编码器、4K超高清HDMI编码器 同三…...

keep-alive缓存组件

keep-alive缓存组件是Vue.js中的一个特殊组件&#xff0c;主要用于缓存内部组件的数据状态&#xff0c;以提高应用的性能和用户体验。以下是关于keep-alive缓存组件的详细解析&#xff1a; 一、作用 缓存组件状态&#xff1a;当组件在<keep-alive>内部切换时&#xff0…...

Linux上如何安装ffmpeg视频处理软件

在Linux上安装ffmpeg需要以下步骤&#xff1a; 更新系统 在开始安装之前&#xff0c;首先需要更新系统以获取最新的软件包列表和版本。在终端中执行以下命令&#xff1a; sudo apt update sudo apt upgrade安装依赖库 ffmpeg依赖于一些库和工具&#xff0c;需要先安装它们。在…...

element如何实现自定义表头?

有时候我们需要实现自定义表头,例如表头里加按钮啥的,这时候就需要用到自定义表头,但是官方对自定义表头的使用写的还是比较简单,今天就来详细说说 在需要使用自定义表头的表头上使用:render-header来启用自定义表头: <el-table-column :render-header="button&…...

OTP防重放攻击

OTP本意是一次性口令&#xff0c;比如邮箱验证码&#xff0c;短信验证码&#xff0c;或者根据totp或者hotp生成的默认30秒一变的6位数字。 不过开发者要注意&#xff0c;必须要在验证成功后失效那个验证码&#xff0c;不然就会导致重放攻击。 对于邮箱验证码&#xff0c;服务器…...

Oracle数据库加密与安全

Wallet简介&#xff1a; Oracle Wallet(即内部加密技术TDE( Transparent DataEncryption&#xff09; TDE是 Oracle10gR2中推出的一个新功能,使用时要保证Oracle版本是在10gR2或者以上 Wallet配置&#xff1a; 1.创建一个新目录&#xff0c;并指定为Wallet目录 /home/oracle…...

【YOLO格式的数据标签,目标检测】

标签为 YOLO 格式&#xff0c;每幅图像一个 *.txt 文件&#xff08;如果图像中没有对象&#xff0c;则不需要 *.txt 文件&#xff09;。*.txt 文件规格如下: 每个对象一行 每一行都是 class x_center y_center width height 格式。 边框坐标必须是 归一化的 xywh 格式&#x…...

应用升级/灾备测试时使用guarantee 闪回点迅速回退

1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间&#xff0c; 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点&#xff0c;不需要开启数据库闪回。…...

(十)学生端搭建

本次旨在将之前的已完成的部分功能进行拼装到学生端&#xff0c;同时完善学生端的构建。本次工作主要包括&#xff1a; 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

HBuilderX安装(uni-app和小程序开发)

下载HBuilderX 访问官方网站&#xff1a;https://www.dcloud.io/hbuilderx.html 根据您的操作系统选择合适版本&#xff1a; Windows版&#xff08;推荐下载标准版&#xff09; Windows系统安装步骤 运行安装程序&#xff1a; 双击下载的.exe安装文件 如果出现安全提示&…...

SpringTask-03.入门案例

一.入门案例 启动类&#xff1a; package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

.Net Framework 4/C# 关键字(非常用,持续更新...)

一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...

在树莓派上添加音频输入设备的几种方法

在树莓派上添加音频输入设备可以通过以下步骤完成&#xff0c;具体方法取决于设备类型&#xff08;如USB麦克风、3.5mm接口麦克风或HDMI音频输入&#xff09;。以下是详细指南&#xff1a; 1. 连接音频输入设备 USB麦克风/声卡&#xff1a;直接插入树莓派的USB接口。3.5mm麦克…...

HTML前端开发:JavaScript 获取元素方法详解

作为前端开发者&#xff0c;高效获取 DOM 元素是必备技能。以下是 JS 中核心的获取元素方法&#xff0c;分为两大系列&#xff1a; 一、getElementBy... 系列 传统方法&#xff0c;直接通过 DOM 接口访问&#xff0c;返回动态集合&#xff08;元素变化会实时更新&#xff09;。…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL

ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...

Visual Studio Code 扩展

Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后&#xff0c;命令 changeCase.commands 可预览转换效果 EmmyLua…...