提示:多练才是王道,加油٩(๑❛ᴗ❛๑)۶

栈Java

    • 1. 栈
    • 2. Java中栈的其中两种实现方式
      • 2.1 Stack类
        • 2.1.1 Stack的模拟实现
      • 2.2 LinkedList类
    • 3. 典型习题讲解
      • 3.1 逆波兰表达式求值
      • 3.2 匹配括号
      • 3.3 合理弹出序列
      • 3.4 最小栈


1. 栈

栈是一种特殊的线性表,其只允许在固定的一端进行插入和删除操作.进行数据插入和删除的一端被称为栈顶,另一端称为栈底.栈中的元素遵循先进后出的原则.栈的三大核心操作:

  1. push(E item):将元素压入栈顶
  2. pop():弹出并返回栈顶元素.如果栈为空,则抛出异常
  3. peek():查看栈顶元素但不弹出.如果栈为空,则抛出异常

在这里插入图片描述

如图所示,在栈顶进行入栈和出栈操作.

2. Java中栈的其中两种实现方式

2.1 Stack类

java.util.Stack类,是Java最早提供的栈实现,官方已经不推荐使用了,但是在很多编程算法题中,Stack仍然被广泛使用.

为什么不推荐?

Stack继承自Vector:

  • 性能差:Vector是线程安全的,所有方法都用synchronized关键字修饰,即使在单线程环境下也会带来不必要的开销.
  • 设计缺陷:它继承制Vector,导致它拥有了许多绯斩操作(如get(int index),insertElementAt),这破坏了栈的封装性和"后进先出"的原则.

既然不推荐,为什么很多刷题平台还是广泛使用呢?

  • 历史关心 & 教学传统:很多算法教程、大学课程和早期的编程书籍都是基于Stack类来讲解栈概念的。这些教学材料有很强的延续性,导致一代代的程序员在学习阶段就习惯了使用Stack。
  • 要求不同:在算法题中,通常只关心核心逻辑的正确性,而不是生产级别的代码质量.Stack的线程安全开销和设计缺陷在算法题的输入规模下几乎可以忽略不计,不会导致超时.
  • 功能足够:对于求解一道算法题Stack提供的 push, pop, peek, empty 方法已经完全够用了。虽然它有多余的方法,但只要你不去用它们,就不会造成问题。

好了言归正传,现在我们来看一下这个Stack:

通过源码可以看到,Stack的核心方法就以下几种:

在这里插入图片描述

也就是:

方法功能
Stack()构造一个空的栈
E push(E e)将e入栈,并返回e
E pop()将栈顶元素出栈并返回(删除)
E peek()获取栈顶元素(瞄一眼,不删除)
int size()获取栈中有效元素个数
boolean empty()检测栈是否为空
2.1.1 Stack的模拟实现
public class MyStack {//底层使用整形数组elem啦存储元素public int[] elem;//usedSize两个作用:①表示当前栈中元素个数②始终指向栈顶元素的下一个位置,下一个入栈元素的下标public int usedSize;//构造方法将数组初始化为大小为10的整型数组public MyStack() {this.elem=new int[10];}//压栈操作public void push(int val) {if(isFull()) {//扩容,使用Arrays.copyOf方法,将新数组长度扩容为原来的2倍elem= Arrays.copyOf(elem,2*elem.length);}elem[usedSize++]=val;}public boolean isFull() {return usedSize==elem.length;}//出栈操作public int pop() {if(empty()) {return -1;//这里更合适做法是抛出一个异常}int toPop=elem[usedSize-1];//保留栈顶元素usedSize--;//逻辑删除return toPop;//最后返回栈顶元素}public boolean empty() {return usedSize==0;}//查看栈顶元素,逻辑与pop一致,除了不用usedSize--public int peek() {if(empty()) {return -1;//这里更合适做法是抛出一个异常}int toPeek=elem[usedSize-1];return toPeek;}
}

2.2 LinkedList类

上面Stack类的底层是动态数组,所以也叫顺序栈,用链表能实现栈吗?—可以.

LinkedList是双向链表,天然实现了栈所需的所有操作,并且效率很高.因此完全可以基于LinkedList实现栈.

在这里插入图片描述

顺着pushpop方法点过去可以看到两者的源码:

在这里插入图片描述

在这里插入图片描述

可以看到,push调用的是addFirst方法,即头插;pop调用的是removeFirst方法,即头删,因此用LinkedList实现的栈栈顶在链表的头,不是尾.

对比使用Stack类(在数组尾部进行入栈出栈,时间复杂度为O(1)):

  • 这样入栈出栈操作也很高效,只涉及改变几个指针的指向,时间复杂度同为O(1).
  • 然而数组满后需要昂贵的扩容操作(申请新数组,复制所有元素),双向链表无扩容开销,且不会造成内存浪费.

3. 典型习题讲解

3.1 逆波兰表达式求值

150. 逆波兰表达式求值 - 力扣(LeetCode)

这是一个栈的典型使用场景之一.求解这道题需要先弄明白逆波兰表达式的计算原理.

首先,什么是逆波兰表达式?

也叫后缀表达式,核心特点是运算符位于其对应的两个操作数之后.我们日常接触的都是中缀表达式,比如:a+b*c+(d*e+f)*g,其对应的后缀表达式为:abc*+de*f+g*+.

这是怎么转化的?有人总结出了以下步骤:

  1. 先将中缀表达式的每个运算周围用括号括起来:a+b*c+(d*e+f)*g->((a+(b*c))+(((d*e)+f)*g))
  2. 然后将每个运算符都向右移到包裹它的括号右边:((a+(b*c))+(((d*e)+f)*g))->((a(bc)*)+(((de)*f)+g)*)+
  3. 最后将括号全部去掉:((a(bc)*)+(((de)*f)+g)*)+->abc*+de*f+g*+

可以明显看到后缀表达式的一个特征:没有括号,第3步将括号全部去掉了.

那为什么需要后缀表达式?

中缀表达式对人类很友好,但是对于计算机却很麻烦:需要处理运算符优先级(如乘除优先于加减)和加括号来确定运算顺序,这个过程比较复杂.

后缀表达式完美地解决了这个问题:

  • 无需括号
  • 无需优先级规则
  • 只需要一个栈即可轻松完成计算.

那它的计算原理是什么?

  1. 首先创建一个栈,准备将后缀表达式按照一定规律从左向右依次压入:
    • 若遇到数字,压入栈中,一直是数字就一直压
    • 若遇到的是运算符(+ - * /),则出栈两次,第一次的是右操作数,第二次是左操作数,运算过后,将结果继续压入栈中
    • 循环上述两个步骤,直至后缀表达式被扫描完
  2. 扫描完后,栈中剩下的即为整个表达式的运算结果

在这里插入图片描述

然后思路就很清晰啦:

class Solution {public int evalRPN(String[] tokens) {Stack<Integer> stack=new Stack<>();for(int i=0;i<tokens.length;i++) {String tmp=tokens[i];if(isOperation(tmp)) {int num2=stack.pop();//右操作数int num1=stack.pop();//左操作数switch(tmp) {case "+":stack.push(num1+num2);break;case "-":stack.push(num1-num2);break;case "*":stack.push(num1*num2);break;case "/":stack.push(num1/num2);break;}}else {Integer num=Integer.valueOf(tmp);//将字符转为整型stack.push(num);}}return stack.pop();//遍历完数组,栈中剩下的就是运算结果}//判断是否是表达式public boolean isOperation(String s) {//引用类型进行比较要用equals,不能用==if(s.equals("+") || s.equals("-") || s.equals("*") || s.equals("/")) {return true;}else {return false;}}
}

3.2 匹配括号

20. 有效的括号 - 力扣(LeetCode)

在这里插入图片描述

该题目要求第一次出现的右括号必须与它的前一个括号匹配,形状相同,朝向相反.

解题思路:创建一个栈,将字符串从左到右依次便利:

  • 若是左括号,就痛快入栈;
  • 若是右括号,弹出栈顶元素与其匹配:
    • 匹配的话(形状相同,朝向相反),就麻溜"出栈走人",然后下一位;
    • 不匹配的话,直接返回false
  • 遍历完了就只用看下栈空不空,空了说明全部都匹配上了,返回true;非空返回false.

思路清晰,coding~

class Solution {public boolean isValid(String s) {Stack<Character> stack=new Stack<>();for(int i=0;i<s.length();i++) {char ch=s.charAt(i);if(ch=='{' || ch=='[' || ch=='(') {//ch为左括号,入栈stack.push(ch);}else {//ch为右括号if(stack.empty()) {return false;//若是遇到了落单的右括号,直接false}else {char tmp=stack.peek();if(tmp=='{' && ch=='}' ||tmp=='[' && ch==']'|| tmp=='(' && ch==')' ) {stack.pop();}else {return false;}}}}return stack.empty();}
}

注意:判断Stack是否为空有两个方法isEmpty()和empty()(empty是Stack自己声明的,isEmpty则是Sytack从其父类Vector继承而来的),在idea上两者都能用,但是在力扣上用isEmpty()会报错,这可能是因为力扣的版本或环境问题,也可能是提供的Stack类是一个预定义的,模拟的类,就像2.1.1的模拟实现一样.

3.3 合理弹出序列

栈的压入、弹出序列_牛客题霸_牛客网

在这里插入图片描述

这种题型在选择题中常见,但是若是要将写选择题那种有点"只可意会不可言传"的感觉转为代码,还是有点差距的~

在这里插入图片描述

这题的思路是:定义两个下标,分别遍历入栈和出栈序列,假设i遍历入栈序列,j遍历出栈序列;

  • 定义一个栈,将i对一个元素入栈,然后将i与j下标对应的值进行比较,若不相等,i++,j不动
  • i一直++,直到i,j对应值相等,此时,将i对应元素出栈,j++
  • 若是栈顶元素与j对应元素一直相等,则一直出栈,j也一直++;不相等了,再重复上一步操作
  • 最后i和j都会遍历完,若此时栈为空,则说明该出栈序列是合理的,反之不合理.
public boolean IsPopOrder (int[] pushV, int[] popV) {Stack<Integer> stack=new Stack<>();int j=0;for(int i=0;i<pushV.length;i++) {stack.push(pushV[i]);while(!stack.empty() && j<popV.length && stack.peek()==popV[j]) {stack.pop();j++;}}return stack.empty();}

3.4 最小栈

155. 最小栈 - 力扣(LeetCode)

在这里插入图片描述

题目明确要求在常数时间内检索到最小元素,若只定义一个栈,那么寻找最小元素势必要遍历一遍stack,时间复杂度为O(n),不符合要求,因此,此题要建立两个栈:

  1. 一个普通栈,正常压入弹出元素,每次使用push,pop,top这三个方法时均要对这个普通栈进行操作.
  2. 还有一个存放最小元素的栈,该栈的压入弹出不能那么随心所欲了:
    • 元素push时要先与该栈栈顶元素比较,只有不大于栈顶元素才能压入栈中,否则便不能;
    • pop时要将栈顶元素与普通栈pop掉的元素比较,若相等,才能pop掉
    • top不能操作该栈
    • getMin只能操作该栈,用于peek该栈的栈顶元素

coding~

class MinStack {Stack<Integer> stack;Stack<Integer> minStack;public MinStack() {stack=new Stack<>();minStack=new Stack<>();}public void push(int val) {stack.push(val);if(minStack.empty()) {minStack.push(val);} else if(val<=minStack.peek()) {minStack.push(val);}}public void pop() {if(stack.empty()) return;int tmp=stack.pop();if(tmp==minStack.peek()) {minStack.pop();}}public int top() {if(stack.empty()) return -1;else return stack.peek();}public int getMin() {if(minStack.empty()) return -1;else return minStack.peek();}
}

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

LayaAir鼠标(手指)控制相机旋转,限制角度

切换天空盒脚本挂载到相机身上 const { regClass, property } Laya;regClass() export class SmoothCameraController extends Laya.Script {declare owner: Laya.Camera;// 旋转灵敏度property({ type: Number, name: "旋转灵敏度" })public rotationSensitivity:…

【数据结构入门】排序算法(4)归并排序

目录 1.排序的原理 1.1 保证子数组有序 1.2 时间复杂度 2. 递归实现 2.1 思路 2.2 代码 3. 非递归实现 3.1 思路 3.2 代码 4.面试题 4.1 题目 4.2 思路 1.排序的原理 归并排序是外排序&#xff0c;所谓外排序就是说能够对文件中的数据进行排序。 ①首先&#xff…

FLEXSPI_Init 硬件故障问题

使用官方例程发现FLEXSPI_Init会引起硬件故障&#xff0c;查阅相关帖子发现主要有两个可能&#xff1a;1、外部闪存配置差异修改 LUT&#xff08;查找表&#xff09;命令&#xff1a;示例中擦除扇区命令为 0xD7&#xff0c;写状态寄存器命令为 0x01&#xff0c;需分别改为 闪存…

如何用 Rust 重写 SQLite 数据库(一):项目探索

要使用 Rust 重写 SQLite 数据库&#xff0c;我们需要实现一个简化的关系型数据库核心功能&#xff08;如 SQL 解析、存储引擎、事务管理&#xff09;。以下是一个分步实践指南&#xff0c;包含关键代码示例。一、项目规划 我们将实现一个超简化数据库 MiniSQL&#xff0c;支持…

JVM之堆(Heap)

一、堆的核心特性 唯一性与共享性 每个JVM实例仅有一个堆&#xff0c;所有线程共享&#xff0c;但可通过线程私有缓冲区&#xff08;TLAB&#xff09;减少多线程分配冲突。内存结构演变 JDK 7及之前&#xff1a;堆分为新生代&#xff08;Young&#xff09;、老年代&#xff08;…

单片机的RAM与ROM概念

RAM与ROM1、RAM与ROM2、 bss、data、heap、stack、text详细讲解3、详细探讨 TCM、OCRAM 和 HBNRAM 之间的区别及其具体作用。3.1、TCM&#xff08;Tightly Coupled Memory&#xff09;3.2、 OCRAM&#xff08;On Chip RAM&#xff09;3.3、HBNRAM (Hibernate RAM)3.4、总结1、R…

实验3:事件处理(2学时)

实验目的&#xff08;1&#xff09;熟练掌握 v-on 指令的用法&#xff0c;学会使用 v-on 指令监听 DOM 元素的事件&#xff0c;并通过该事件触发调用事件处理程序。&#xff08;2&#xff09;掌握v-on 指令修饰符的基本用法。实验内容实现购物车功能的拓展&#xff08;商品数量…

商品库存扣减方案

文章目录1. Lua脚本 Redis&#xff08;业界首选&#xff0c;综合最优&#xff09;2. Redis原子命令&#xff08;DECRBY 结果校验&#xff09;3. Redis事务&#xff08;MULTI/EXEC&#xff09;4. 分布式锁&#xff08;基于Redis实现&#xff09;5. Redisson客户端封装&#xf…

关于在阿里云DMS误操作后如何恢复数据的记录

前言 昨天因客户员工操作错误&#xff0c;导致快递单号和订单互换。客户员工那边让笔记修改数据。 于是笔者写下如下SQL来操作&#xff0c;导致了灾难性事故。 update t_order_fed_ex_record set tracking_number 884102170661, master_tracking_number 884102170661, push…

【操作系统核心知识梳理】线程(Thread)重点与易错点全面总结

在多任务操作系统中&#xff0c;线程是比进程更轻量的执行单元&#xff0c;理解线程的特性和实现方式是掌握并发编程的基础。本文系统梳理了线程相关的核心知识点和常见误区&#xff0c;助你夯实操作系统基础。一、线程的基本概念与引入目的 1.1 什么是线程&#xff1f; 线程是…

深入理解 Python 中的 `__call__` 方法

化身为可调用的对象&#xff1a;深入理解 Python 中的 __call__ 方法 引言&#xff1a;函数与对象的边界模糊化 在 Python 中&#xff0c;我们最熟悉的概念莫过于函数&#xff08;Function&#xff09; 和对象&#xff08;Object&#xff09;。函数是可调用的&#xff08;calla…

云服务器使用代理稳定与github通信方法

使用SSH反向隧道 (SSH Reverse Tunneling) 利用SSH连接在您的本地电脑和云服务器之间建立一个反向的加密通道。 原理&#xff1a; 从本地电脑发起一个SSH命令到您的云服务器&#xff0c;这个命令会告诉云服务器&#xff1a;“请监听您自己的某个端口&#xff08;例如&#xff1…

7.k8s四层代理service

Service的基本介绍 Cluster IP&#xff1a;每个 Service 都分配了一个Cluster IP&#xff0c;它是一个虚拟的内部IP地址&#xff0c;用于在集群内部进行访问。这个虚拟IP是由Kubernetes自动分配的&#xff0c;并且与Service对象一一对应。 端口映射&#xff1a;Service可以映射…

Qt 工程中 UI 文件在 Makefile 中的处理

Qt 工程中 UI 文件在 Makefile 中的处理 在 Qt 工程中&#xff0c;.ui 文件&#xff08;Qt Designer 界面文件&#xff09;需要通过 uic&#xff08;用户界面编译器&#xff09;工具转换为对应的头文件。以下是几种情况下如何处理 UI 文件&#xff1a;1. 使用 qmake 自动生成 M…

ZLMediaKit性能测试

一、环境 系统&#xff1a;虚拟机 Ubuntu22.04 64bit配置: 4核8G设置&#xff1a;ulimit -n 102400 二、安装 依赖安装sudo apt update sudo apt install ffmpeg sudo apt install nloadzlm服务安装参考&#xff1a;https://blog.csdn.net/hanbo622/article/details/149064939?…

智能文档处理业务,应该选择大模型还是OCR专用小模型?

智能文档处理业务中&#xff0c;最佳策略不是二选一&#xff0c;而是“大小模型协同”。用专用小模型处理高频、标准化的核心文档流&#xff0c;实现极致效率与成本控制&#xff1b;用大模型赋能非标、长尾文档的灵活处理&#xff0c;加速业务创新。 OCR小模型会被大模型取代吗…

android 如何判定底部导航栏显示时 不是键盘显示

在 Android 中判定底部导航栏是否显示时&#xff0c;核心痛点是 区分 “导航栏的底部 Insets” 和 “软键盘弹出的底部 Insets”—— 两者都会导致 getSystemWindowInsetBottom() 返回非零值&#xff0c;直接判断会误将键盘弹出当成导航栏显示。以下是基于 WindowInsets 类型区…

你知道服务器和电脑主机的区别吗?

我们都知道服务器和台式主机有着不同之处&#xff0c;但具体说出个一二三来很多人还是一头雾水&#xff0c;也就是知其然不知其所以然&#xff0c;都是CPU主板 内存 硬盘 电源&#xff0c;撑死就差一个显卡不同&#xff0c;但其实服务器和我们正常使用的台式主机差距很大&#…

什么是包装类

什么是包装类 在Java中&#xff0c;包装类&#xff08;Wrapper Class&#xff09;是为基本数据类型提供的对应的引用类型。Java中的基本数据类型&#xff08;如int、char、boolean等&#xff09;不是对象&#xff0c;为了在需要对象的场景中使用基本数据类型&#xff08;如集合…

用Python打造专业级老照片修复工具:让时光倒流的数字魔法

在这个数字化时代&#xff0c;我们手中珍藏着许多泛黄、模糊、甚至有划痕的老照片。这些照片承载着珍贵的回忆&#xff0c;但时间的侵蚀让它们失去了往日的光彩。今天&#xff0c;我将带您一起用Python开发一个专业级的老照片修复工具&#xff0c;让这些珍贵的记忆重现光彩。为…