【数据结构篇】线性表2 —— 栈和队列
前言:上一篇我们介绍了顺序表和链表
(https://blog.csdn.net/iiiiiihuang/article/details/132615465?spm=1001.2014.3001.5501),
这一篇我们将介绍栈和队列,栈和队列都是基于顺序表和链表来实现的
目录
栈(Stack)
什么是栈 ?
栈的方法 和 使用
栈的模拟实现
先初始化一下栈
往栈里插入元素 (push)
栈是否为空(empty)
弹出栈顶元素(删除)(pop)
获取栈顶元素 (peek)
模拟实现完整代码
栈的应用场景
1. 改变元素的序列
2. 将递归转化为循环
补充 :
队列(Queue)
什么是队列 ?
队列的方法
队列模拟实现
初始化
offer
poll
peek
完整代码(链式队列)
循环队列
认识循环队列
如何把数组看作是一个循环呢?(rear 从 7 --> 0)
设计一个循环队列 (https://leetcode.cn/problems/design-circular-queue/)
初始化
判断队列是否是空的,和是否是满的
入队
出队
得到队头元素
得到队尾元素
完整代码: (循环队列)
双端队列 (Deque)
(面试题) 1. 用队列实现栈 2. 用栈实现队列
1. 用队列实现栈
代码实现:
入栈
出栈
获取栈顶元素
判断栈空
完整代码
2. 用栈实现队列
我直接上代码了:
栈(Stack)
什么是栈 ?
栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。
后进先出原则:先进的后出,后进的先出 。我们举一个生活中的例子
比如这有个圆筒,一端封闭,一端开口,然后往圆筒里放乒乓球:
你想拿乒乓球,先拿到的肯定是后放的,那如果你想要拿到最底下的,你肯定得把上面的都拿走才行。也就是 栈的后进先出原则 (先进的后出,后进的先出)
栈的方法 和 使用
栈的方法很少,只有这些:


使用 (没什么好说的😁😁)

栈的模拟实现
栈这种数据结构可以用数组来实现,也可以用链表来实现,这里我们用数组实现。

先初始化一下栈

往栈里插入元素 (push)
和顺序表异曲同工,先看看满没满,满了要扩容,再入栈

栈是否为空(empty)

弹出栈顶元素(删除)(pop)
就很简单 (顺序表那搞懂,这些方法很简单的)

获取栈顶元素 (peek)

模拟实现完整代码
import java.util.ArrayList;
import java.util.Arrays;/*** @Author: iiiiiihuang*/
public class MyStack {private int[] elem;private int usedSize;private static final int DEFAULT_CAPACITY = 10;//默认容积public MyStack() {this.elem = new int[DEFAULT_CAPACITY];}/*** 往栈里插入元素* @param val*/public void push(int val) {isFull();this.elem[usedSize] = val;usedSize++;}private void isFull() {if(usedSize == this.elem.length) {this.elem = Arrays.copyOf(this.elem, 2*this.elem.length);}}/*** 弹出栈顶元素* @return*/public int pop() {if(empty()) {throw new EmptyException("栈为空");}int pop = this.elem[usedSize - 1];usedSize--;return pop;}/*** 获取栈顶元素* @return*/public int peek() {if(empty()) {System.out.println("栈为空");return 0;}int peek = this.elem[usedSize - 1];return peek;}/*** 栈是否为空* @return*/public boolean empty() {return usedSize == 0;}
}
栈的应用场景
1. 改变元素的序列
我们去牛客上找两道题做做

根据我们上面介绍,栈 遵循 先进后出原则, 我们可以得出答案,不可能的出栈顺序是 D 选项
因为F,E出来了,下一个出来的必须是 D,这种情况下C不可能比D先出。

2. 将递归转化为循环
比如逆序打印链表:
1.递归方法
2.循环方法
运行结果

补充 :
用数组实现栈,出栈,入栈,都是O(1),
那用链表如何实现栈呢?
1.假设用单链表(单向)实现栈:
尾插:
入栈:尾插法 入栈是 O(n)
出栈:因为栈后进的先出,这就相当于删除链表的尾巴节点,时间复杂度还是O(n);
头插:
入栈:头插法 O(1)
出栈:相等于删除头节点,时间复杂度O(1)
2.假设用双向链表实现栈: (前后都能找到)
尾插:
入栈:尾插法 入栈是 O(1)
出栈:时间复杂度还是O(1),
头插:
入栈:头插法 O(1)
出栈:时间复杂度O(1)
上述我们可知,用链表实现栈时,双向链表实现栈是最好的,不管从那边入栈,出栈 时间复杂度都是O(1)。
那使用链式栈时就可以这样使用:如果这是个栈,那peek的内容就是 4.
是 4.
那为啥会这样,你看LinkedList,里本来就有栈里的一些方法,push.....
push 用的是头插法
所以如果是顺序栈,你可以用Stack, 如果是链式栈,你就用LinkedList
队列(Queue)
什么是队列 ?
队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO(First In First Out)
入队列:进行插入操作的一端称为队尾(Tail/Rear) 出队列:进行删除操作的一端称为队头
举例说明:
假如有一条单行隧道,车辆必须从入口进 (入队列),从出口出(出队列)。那先驶出隧道的车辆肯定是先进去的,后驶出的肯定是后进去的,这就是 队列的先进先出原则

通过上述介绍,我们发现我们可以把 双向链表当成是一个 队列 ,(单链表也行,双链表更简单)
队尾进,队头出,时间复杂度O(1)
ps:Queue 普通队列,Deque 双端队列(两边都可以进出),LinkedList 可以当作普通队列,双端队列,链表,栈(究极打工人😭😭😭)
队列的方法
在Java中,Queue是个接口,底层是通过链表实现的。
队列方法少的嘞
offer (入队列)
peek
poll
.........
add,remove ,element (和peek一个意思)是一组
offer, poll, peek 是一组
队列模拟实现
队列的实现使用 顺序结构 和 链式结构 都行,但链式结构更简单一点。
我们这里拿双链表去写 (前面的链表学会了,这里很简单的,信手拈来)
初始化

offer

调试一看 插进去了
poll
peek
完整代码(链式队列)
/*** @Author: iiiiiihuang*/
public class MyQueue {static class ListNode {private int val;private ListNode prev;private ListNode next;public ListNode(int val) {this.val = val;}}private ListNode front;//头private ListNode rear;//尾private int usedSize;//计数/*** 插入--- 头插法* @param val*/public void offer(int val) {ListNode node = new ListNode(val);if(front == null) {front = node;rear = node;} else {node.next = front;front.prev = node;node = front;}usedSize++;}/*** 删除 -- 尾删* @return*/public int poll() {if(front == null) {throw new EmptyException("队列为空");}if(front == rear) {int poll = front.val;front = null;rear = null;usedSize--;return poll;}int poll = rear.val;rear = rear.prev;rear.next = null;usedSize--;return poll;}/*** 获取队头* @return*/public int peek() {if(front == null) {throw new EmptyException("队列为空");}return rear.val;}
}
循环队列
实际中我们有时还会使用一种队列叫循环队列。
环形队列通常使用数组实现。
认识循环队列
。。。。。
这就有意思了 ,
front 和 rear 第一次在一起的时候 队列 是空的,第二次 在一起的时候是队列是满的
那这怎么判断呢,其实有很多方法去判断
1.计数 usedSize == len
2.标记,flag
3.浪费一个空间来区分(常用)
浪费一个空间,就类似下面这样

如何把数组看作是一个循环呢?(rear 从 7 --> 0)
就是把 rear ++ 换成 (rear + 1 )% len (余数肯定都是小于 len的(len 数组长度)),rear++ 会越界的 (看不懂自己带数据算算)
设计一个循环队列 (https://leetcode.cn/problems/design-circular-queue/)
根据这个来

初始化
判断队列是否是空的,和是否是满的

入队

出队

得到队头元素

得到队尾元素

不能直接rear - 1哦
那现在我们放到力扣里运行一下(链接在上边)
错了 !!! 为什么,我们看看我什么
数组容量是 3 ,预期结果成功 插入了 3个,而我们的程序,只能插入两个,因为我们的浪费掉了一个空间 。
所以我们应该多给一个空间:

在运行一下 ,通过了,当然你要是觉得这样奇怪,你也可以用 usedSize,很多方法啦😎😎
完整代码: (循环队列)
class MyCircularQueue {private int[] elem;private int front;//头private int rear;//尾public MyCircularQueue(int k) {this.elem = new int[k + 1];}public boolean enQueue(int value) {if(isFull()) {return false;}elem[rear] = value;rear = (rear + 1) % elem.length;return true;}public boolean deQueue() {if(isEmpty()) {return false;}front = (front + 1) % elem.length;return true;}public int Front() {if(isEmpty()) {return -1;}int ret = elem[front];return ret;}public int Rear() {if(isEmpty()) {return -1;}int index = (rear == 0) ? elem.length - 1 : rear - 1;//不能直接rear - 1哦return elem[index];}public boolean isEmpty() {return front == rear;}public boolean isFull() {if((rear + 1) % this.elem.length == front) {return true;}return false;}
}
双端队列 (Deque)
双端队列(deque)是指允许两端都可以进行入队和出队操作的队列,deque 是 “double ended queue” 的简称。
那就说明元素可以从队头出队和入队,也可以从队尾出队和入队。
Deque是一个接口,使用时必须创建LinkedList的对象 。
在实际工程中,使用Deque接口是比较多的,栈和队列均可以使用该接口
1. Deque<Integer> stack = new ArrayDeque<>();//双端队列的线性实现(数组)
2. Deque<Integer> queue = new LinkedList<>();//双端队列的链式实现
(面试题) 1. 用队列实现栈 2. 用栈实现队列
1. 用队列实现栈
225. 用队列实现栈 - 力扣(LeetCode)
1.首先一个栈是不够用的,它俩的出栈原则不一样,栈先进后出,队列先进先出,一个队列实现不了呀 ,对不上。
2.所以我们需要两个队列,当 2 个队列都为空的时候,说明模拟栈为空。
3.当要出栈的时候,(出 45 ),我们要把 12,23,34,入到第二个队列里去,然后再把第一个队列里的 45 出队列 ,
结论就是,”出栈“的时候出不为空的 队列,出队列size - 1 个(俩队列轮流),最后那一个就是我们要 ”出栈“的元素
4.“入栈” 的时候入到不为空的队列
思路有了,我们开始写代码:
代码实现:
入栈
出栈
用for 循环的时候注意一下,不要写成 i < queue.size() - 1,因为queue.size()一直在变。你先记录一下

获取栈顶元素
我们可以定义一个临时量,来存放栈顶元素
里面的元素一直被覆盖,那最后出队列的元素,就覆盖了之前的全部元素,就是栈顶元素。
注意:这里可别写成 queue1.offer(queue2.poll), 跳两回啦。

判断栈空

完美通过

完整代码
import java.util.LinkedList;
import java.util.Queue;/*** @Author: iiiiiihuang*/
public class MyStack {private Queue<Integer> queue1;private Queue<Integer> queue2;public MyStack() {queue1 = new LinkedList<>();queue2 = new LinkedList<>();}/*** 入栈操作* @param x*/public void push(int x) {if(!queue1.isEmpty()){queue1.offer(x);} else if(!queue2.isEmpty()) {queue2.offer(x);}else {queue1.offer(x);}}/*** 出栈* @return*/public int pop() {//先判断栈空不空if(empty()) {return -1;}if(!queue1.isEmpty()) {int len = queue1.size() - 1;while (len > 0) {int tmp = queue1.poll();queue2.offer(tmp);len--;}return queue1.poll();} else {int len = queue2.size() - 1;while (len > 0) {int tmp = queue2.poll();queue1.offer(tmp);len--;}return queue2.poll();}}/*** 获取栈顶元素* @return*/public int top() {int tmp = -1;if(empty()) {return -1;}if(!queue1.isEmpty()) {int len = queue1.size();while (len > 0) {tmp = queue1.poll();queue2.offer(tmp);len--;}return tmp;} else {int len = queue2.size();while (len > 0) {tmp = queue2.poll();queue1.offer(tmp);len--;}return tmp;}}public boolean empty() {return queue1.isEmpty() && queue2.isEmpty();}
}
2. 用栈实现队列
232. 用栈实现队列 - 力扣(LeetCode)
思路差不多
1.两个栈实现,同理
2.入队的时候 把所有元素 全部放到第一个栈当中
3.出队的时候 把所有的元素 全部倒到第二个栈当中,然后出第二个栈顶元素就行了.(记得判空)
我直接上代码了:
import java.util.Stack;/*** @Author: iiiiiihuang*/
public class MyQueue {private Stack<Integer> stack1;private Stack<Integer> stack2;public MyQueue() {stack1 = new Stack<>();stack2 = new Stack<>();}public void push(int x) {stack1.push(x);}public int pop() {if(empty()) {return -1;}if(stack2.isEmpty()) {while (!stack1.isEmpty()) {stack2.push(stack1.pop());}}return stack2.pop();}public int peek() {if(empty()) {return -1;}if(stack2.isEmpty()) {while (!stack1.isEmpty()) {stack2.push(stack1.pop());}}return stack2.peek();}public boolean empty() {return stack1.isEmpty() && stack2.isEmpty();}
}

先别急着走,点个关注先,👀👀👀
点赞,评论,收藏,支持一下ヾ(≧▽≦*)oヾ(≧▽≦*)o
相关文章:
【数据结构篇】线性表2 —— 栈和队列
前言:上一篇我们介绍了顺序表和链表 (https://blog.csdn.net/iiiiiihuang/article/details/132615465?spm1001.2014.3001.5501), 这一篇我们将介绍栈和队列,栈和队列都是基于顺序表和链表来实现的 目录 栈ÿ…...
万物互联:软件与硬件的协同之道
在当今数字化时代,我们身边的一切似乎都与计算机和互联网有关。从智能手机到智能家居设备,从自动驾驶汽车到工业生产线,无论我们走到哪里,都能看到软件和硬件的协同作用。本文将探讨这种协同作用,解释软件和硬件如何相…...
ping: www.baidu.com: Name or service not known 写了DNS还是不行
环境描述:ESXI平台上,一台Centos7虚拟主机。 问题描述:平台上的其他的虚拟机可以正常ping通,就这台ping IP地址可以通,ping域名解析失败。 排查过程: 1、检查网卡配置文件和/etc/resolv.conf配置文件是否…...
C++中的decltype、std::declval 和 std::decay_t傻傻分不清楚
文章目录 前言它们是什么通俗解释总结 前言 在C中提到推导第一个映入脑海的可能是“模板”,当然有人也可能想到 auto,这些都是和推导相关的语言语法,再比如“完美转发”等等,总是就是他们的类型不用明明白白的写出来,…...
什么是Ubuntu LTS?与常规版本的区别
Ubuntu LTS(Long-Term Support)是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性。与常规的Ubuntu版本相比,LTS版本在以下几个方面有所不同: 支持周期更长: 使用Ubuntu LTS版本,…...
如何写一个可以找到工作的简历不至于太烂
简历是自己的一个很重要的标签,是获得面试的敲门砖,简历是要时常更新的,否则会错过一些机会。简历也是给自己的正反馈。 方法 ● 模仿,例如Boss,拉钩下面都给你一个案例模板供你参考,但是我觉得其实参考性…...
el-select 使用
案例: /* * label : 界面上展示的是哪个字段,我这里需要展示名称 * value : 绑定的字段,一般是id */<el-selectv-model"Form.BillNumber"placeholder"请选择"change"changeValue($event)"><el-optionv-for"…...
思维导图怎么变成ppt?4个思维导图一键生成ppt的方法
做好的思维导图如何变成一份ppt?本文罗列了4个可行方法,一起来看看吧。 一 直接复制粘贴 这是最简单的方法,虽然这样可能会花费一些时间,但可以确保内容排版和布局与你想要的一致。当然,我们大可使用更高效的方法。…...
3D点云处理:点云投影为2D图像 调平点云(附源码)
文章目录 0. 测试效果1. 基本内容1.1 计算点云位姿1.2 调平点云1.3 点云投影2. 代码实现文章目录:3D视觉个人学习目录微信:dhlddxB站: Non-Stop_0. 测试效果...
mysql 查询优化 、索引失效
查询优化 物理查询优化 通过索引和表连接方式等技术来进行优化,这里重点需要掌握索引的使用 逻辑查询优化 通过SQL 等价变换 提升查询效率,直白一点就是说,换一种查询写法执行效率可能更高 索引失效 计算、函数、类型转换(自动或…...
支付宝pc支付(springboot版),简单配置即可实现支付
概述 支付宝pc支付,只需要修改配置就可以实现支付,0基础小白都可以用。使用springboot编写,简单易用。 详细 DEMO简介 springboot整合支付宝pc支付,仅仅需要少量的配置,就可以实现pc支付。 项目截图 支付流程 用户…...
【Redis专题】Redis持久化、主从与哨兵架构详解
目录 前言课程目录一、Redis持久化1.1 RDB快照(Snapshot):二进制文件基本介绍开启/关闭方式触发方式bgsave的写时复制(COW,Copy On Write)机制优缺点 1.2 AOF(append-only file)&…...
【vue2第十三章】自定义指令 自定义v-loading指令
自定义指令 像 v-html,v-if,v-for都是vue内置指令,而我们也可以封装自定义指令,提升编码效率。 什么是自定义指令? 自己定义的一些指令,可以进行一些dom操作,扩展格外的功能。比如让图片懒加载…...
数据结构--6.3查找算法(静态、动态)(插值查找)
静态查找:数据集合稳定,不需要添加,删除元素的查找操作。 动态查找:数据集合在查找的过程中需要同时添加或删除元素的查找操作。 对于静态查找来说,我们不妨可以用线性表结构组织数据,这样可以使用顺序查找…...
Spring Boot日志基础使用 设置日志级别
然后 我们来说日志 日志在实际开发中还是非常重要的 即可记录项目状态和一些特殊情况发生 因为 我们这里不是将项目 所以 讲的也不会特别深 基本还是将Spring Boot的日志设置或控制这一类的东西 相对业务的领域我们就不涉及了 日志 log 初期最明显的作用在于 开发中 你可以用…...
Playwright for Python:断言
一、支持的断言 Playwright支持以下几种断言: 断言描述expect(locator).to_be_checked()复选框被选中expect(locator).to_be_disabled()元素是禁用状态expect(locator).to_be_editable()元素是可编辑状态expect(locator).to_be_empty()容器是空的expect(locator).…...
websocket--技术文档--spring后台+vue基本使用
阿丹: 给大家分享一个可以用来进行测试websocket的网页,个人觉得还是挺好用的. WebSocket在线测试工具 还有一个小家伙ApiPost也可以进行使用websocket的测试。 本文章只是基本使用--给大家提供思路简单实现!! 使用spring-boot建立一个服…...
day01-ES6新特性以及ReactJS入门
课程介绍 ES6新特性ReactJS入门学习 1、ES6 新特性 1.2、let 和 const 命令 var 之前,我们写js定义变量的时候,只有一个关键字: var var 有一个问题,变量作用域的问题,作用域不可控,就是定义的变量有时会…...
MySQL5.7慢查询实践
总结 获取慢查询SQL 已经执行完的SQL,检查慢查询日志,日志中有执行慢的SQL正在执行中的SQL,show proccesslist;,结果中有执行慢的SQL 慢查询日志关键参数 名称解释Query_time查询消耗时间Time慢查询发生时间 分析慢查询SQL e…...
MySQL数据库的增删改查(进阶)
目录 数据库约束 约束类型 NULL约束 UNIQUE:唯一约束 DEFAULT:默认值约束 PRIMARY KEY:主键约束 FOREIGN KEY:外键约束 表的设计 一对一关系 一对多关系 多对多关系 查询 聚合查询 聚合函数 GROUP BY子句 HAVING …...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...
工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
智慧工地云平台源码,基于微服务架构+Java+Spring Cloud +UniApp +MySql
智慧工地管理云平台系统,智慧工地全套源码,java版智慧工地源码,支持PC端、大屏端、移动端。 智慧工地聚焦建筑行业的市场需求,提供“平台网络终端”的整体解决方案,提供劳务管理、视频管理、智能监测、绿色施工、安全管…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP
编辑-虚拟网络编辑器-更改设置 选择桥接模式,然后找到相应的网卡(可以查看自己本机的网络连接) windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置,选择刚才配置的桥接模式 静态ip设置: 我用的ubuntu24桌…...
