【Java 数据结构】栈的实现及相关OJ题

news2024/11/15 23:33:17

🎉🎉🎉点进来你就是我的人了
博主主页:🙈🙈🙈戳一戳,欢迎大佬指点!

人生格言:当你的才华撑不起你的野心的时候,你就应该静下心来学习!

欢迎志同道合的朋友一起加油喔🦾🦾🦾
目标梦想:进大厂,立志成为一个牛掰的Java程序猿,虽然现在还是一个🐒嘿嘿
谢谢你这么帅气美丽还给我点赞!比个心


目录

1. 什么是栈?(Stack)

2. 栈的使用

3. 模拟实现一个栈

3.1 构造方法和成员属性

3.2 push 方法

3.3 pop 方法

3.4 peek 方法

3.5 empty 方法

4.栈相关的OJ题

   4.1 有效的括号

  4.2 栈的压入弹出序列

  4.3 逆波兰(后缀)表达式求值

  4.4 最小栈



1. 什么是栈?(Stack)

 栈是一种数据结构,是操作受限制的线性表(先进后出),它只能在固定的一端进行插入和删除操作,进行数据插入和删除的一端为栈顶,另一端为栈底,按存储方式分为顺序栈(更好实现)和链式栈。

压栈:栈的插入操作叫做进栈/压栈/入栈。入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

2. 栈的使用

方法功能说明
Stack()构造一个空栈
E push(E e)将e入栈并返回e
E pop()将栈顶元素出栈并返回
E peek()获取栈顶元素
int size()获取栈中有效元素的个数
boolean empty()检测栈是否为空

  ●顺序栈:Deque接口+实现类ArrayDeque

Deque<Integer> deque = new ArrayDeque<>();//顺序栈

 ●链式栈:Deque接口+实现类LinkedList

Deque<Integer> deque1 = new LinkedList<>();//链式栈

   栈的使用建议使用上面两种方式,不建议使用Stack

3. 模拟实现一个栈

3.1 构造方法和成员属性

public class MyStack {
    private int[] elem; //存放数据的数组
    private int size; //有效元素个数
    private static final int DEFAULT_CAPACITY = 10; //约定好的默认容量
 
    public MyStack() {
        this.elem = new int[DEFAULT_CAPACITY];
        this.size = 0;
    }
}

有了顺序表和链表的学习,再次学习栈是很轻松的,但是在源码中,开辟空间是在 push 中开辟的,跟顺序表类似的。

3.2 push 方法

// 压栈
public int push(int data) {
    // 判断是否需要增容
    if (this.size == this.elem.length) {
        this.elem = Arrays.copyOf(this.elem, this.size * 2);
    }
    // 压栈只能往栈顶压栈
    this.elem[this.size++] = data;
    return data;
}

在实现push方法要注意,栈扩容的问题,因为我们底层使用的是数组,所以可以用 Arrays.copyOf方法进行扩容。 

3.3 pop 方法

// 出栈
public int pop() {
    // 判断栈是否为null
    if (this.size == 0) {
        throw new MyStackEmptyException("栈为空!");// 自定义异常
    }
    return this.elem[--this.size];
}

在出栈方法中,如果栈为null的情况是不能进行出栈的,也就是有效元素个数size为0的情况,这里博主是直接抛出了一个自定义异常。在返回值的地方也要注意,出栈后栈的元素会减少一个,但我们只需要设置有效数据减一个即可,就像计算机中的删除一样,本质是将数据设置成无效,有新的数据可以直接覆盖。

3.4 peek 方法

// 查看栈顶元素
public int peek() {
    // 判断栈是否为null
    if (this.size == 0) {
        throw new MyStackEmptyException("栈为空!"); //自定义异常
    }
    return this.elem[this.size - 1];
}

peek方法与pop方法相差无几,需要注意的是在返回栈顶元素的时候不要使有效数据个数减少了就像。

3.5 empty 方法

// 判断栈是否为空
public boolean empty() {
    return this.size == 0;
}

4.栈相关的OJ题

   4.1 有效的括号

        ●题目展示

       ● 写题链接:20. 有效的括号 - 力扣(LeetCode)

       ● 写题思路:

           1) 新建一个栈存左括号,得到对应右括号出左括号,这次匹配成功,继续匹配

           2) 只要左括号的栈为空,且给定字符串还没遍历完,那就代表右括号多了

           3) 给定字符串遍历完了,但是存左括号的栈还有剩余的左括号,那么就是左括号多了

           4) 只要给定字符串遍历完毕,且存左括号的栈无剩余,则为有效括号。

        ● 图示

       ● 实现代码:

class Solution {
    public boolean isValid(String s) {
       Deque<Character> leftStack = new ArrayDeque();
       for(int i = 0; i < s.length(); i++) {
           char ch = s.charAt(i);
           if(ch == '(' || ch == '[' || ch == '{') {
               leftStack.push(ch);
           }else {
               //如果栈中无左括号,又得到了一个右括号,则无效
               if(leftStack.isEmpty()) { //多余的右括号
                   return false;
               }
               //ch为字符串中得到的右括号,ch2为栈中的左括号
               char ch2 = leftStack.peek();
               if(ch == ')' && ch2 == '(' || ch == ']' && ch2 == '[' || ch == '}' && ch2 == '{') {
                   //出栈
                   leftStack.pop();
               }else {
                   return false;//不匹配
               }
           }
       }
       if(!leftStack.isEmpty()) { //还有多余的左括号
           return false;
       }
       return true;
    }
}

  4.2 栈的压入弹出序列

        ● 题目展示


       ● 写题链接:栈的压入、弹出序列_牛客题霸_牛客网 (nowcoder.com)

       ● 写题思路:

           1) 新建一个栈,根据给定的出栈序列依次进行匹配。

           2) 只要不等于出栈序列的数字,就一直遍历入栈序列入栈

           3) 等于出栈序列的数字后,直接出栈,只要等于出栈序列的数字,该栈就一直出栈,直到不等于结束出栈,继续遍历给定的入栈序列进行入栈。

           4) 重复该操作直到遍历完入栈序列,最后这个栈如果为空,则匹配成功,反之则匹配失败

        ● 图示

         ● 实现代码:

public class Solution {
    public boolean IsPopOrder(int [] pushA,int [] popA) {
      Stack<Integer> stack = new Stack<>();
          int j = 0;
          for(int i = 0;i < n;i++){
            stack.push(pushA[j]);
            //此处用equals是因为Integer值的取值范围在-128~127
            while(j < pop.length && (stack.isEmpty()||stack.peek().equals(popA[i])){
                stack.pop();
                j++;
            }
        return stack.empty();
    }
}

  4.3 逆波兰(后缀)表达式求值

         ● 题目展示


       ● 写题链接:150. 逆波兰表达式求值 - 力扣(LeetCode)

       ● 写题思路:

          1) 准备中间栈遍历给定后缀表达式,遇到操作数则入中间栈

          2) 遇到操作符则出栈两个操作数进行运算,并将运算结果入栈

          3) 直到后缀表达式遍历完毕,栈中最后一个元素值则为计算结果

        ● 知识普及

         在讲解这个题之前给大家普及一点知识,我们平时的算术表达式中,运算符总是出现在两个操作数之间,例如 5 * (7 - 2 * 3) + 8 / 2 ,这种形式叫做中缀表达式,而我们的逆波兰表达式又叫后缀表达式,那什么是后缀表达式呢???

我们把中缀表达式的运算符移动到自己所在的括号的右括号的右边,然后再去括号,这就是逆波兰表达式,,,那这个东西该怎么计算呢??? 

       ● 实现代码:

class Solution {
    public int evalRPN(String[] tokens) {
        Deque<Integer> stack = new ArrayDeque<Integer>(); //存操作数
        for(int i = 0; i < tokens.length; i++) {
            String ch = tokens[i];
            if(!isOperators(ch)) {
                stack.push(Integer.parseInt(ch));
            }else {
                int num1 = stack.pop();//出两个操作数
                int num2 = stack.pop();
                int result = 0; //计算结果  
                switch(ch) {
                    case "+":
                    result = num1 +num2;
                    break;
                    case "-":
                    result = num2 - num1;
                    break;
                    case "*":
                    result = num1 * num2;
                    break;
                    case "/":
                    result = num2 / num1;
                    break;
                }
                //结果入栈
                stack.push(result);
            }
        }
        return stack.pop();
    }
 
    //判断是否为操作符
    public boolean isOperators(String s) {
        return s.equals("+") || s.equals("*") || s.equals("-") || s.equals("/");
    }
}

  4.4 最小栈

          ● 题目展示

         ● 写题链接:力扣

         ● 写题思路:题目的目的是想让我们在O(1)时间复杂度内拿到栈的最小值,所以这里我们不能去遍历栈,而是需要用到两个栈,下面用画图的形式分析一下如何做到:

         ● 实现代码:

class MinStack {
    private Stack<Integer> stack1;
    private Stack<Integer> MinStack;
 
    public MinStack() {
        stack1 = new Stack<>();
        MinStack = new Stack<>();
    }
 
    public void push(int val) {
        stack1.push(val);
        if(MinStack.empty()) {
            MinStack.push(val);
        } else {
            if(val <= MinStack.peek()) {
                MinStack.push(val);
            }
        }
    }
    //题目规定了栈不为空,所以这里没判断
    public void pop() {
        int tmp = stack1.pop();
        if(tmp == MinStack.peek()) {
            MinStack.pop();
        }
    }
    //题目规定了栈不为空
    public int top() {
        return stack1.peek();
    }
 
    public int getMin() {
        return MinStack.peek();
    }
}

5. 栈,虚拟机栈,栈帧的区别

栈:一种后进先出的数据结构,继承自Vector

虚拟机栈:具有特殊作用的一块内存空间。栈区:是线程私有的,存放的是函数调用相关信息,主要是栈帧,它是按照数据结构中栈的特性来实现的

栈帧:一种结构,与函数调用相关。内部:局部变量表,操作数栈。每个方法运行时JVM会创建一个栈帧,然后将栈帧压到虚拟机栈中,调用结束时,该方法对应的栈帧会从虚拟机栈中出栈。

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

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

相关文章

Flutter 通过 VS code 连接 Android 模拟器(Windows)

环境配置 Flutterhttps://flutter.cn/docs/get-started/install/windowsAndroid Studiohttps://developer.android.google.cn/studioVS code安装Flutter插件https://flutter.cn/docs/get-started/editor?tabvscode夜神模拟器https://www.yeshen.com 注意事项 Flutter安装之…

第五章_Redis事务

是什么 官网 能做什么 一个队列中&#xff0c;一次性、顺序性、排他性的执行一系列命令 Redis事务 VS 数据库事务 1 单独的隔离操作 Redis的事务仅仅是保证事务里的操作会被连续独占的执行&#xff0c;redis命令执行是单线程架构&#xff0c;在执行完事务内所有指令前是不可…

【浅学 Linux】

浅学 Linux 一、 Linux1. 目录2. 虚拟机的设置2.1 克隆与快照 3.系统与设置命令3.1 用户相关命令3.2 用户组3.3 管理用户组内成员3.4 日期管理3.5 显示用户3.6 id命令&sudo命令3.7 进程相关的命令top 查看进程ps 查看进展kill 杀死进程 4. Linux目录管理4.1 关机与重启命令…

RIP协议(路由信息协议)简介

一.动态路由简介 1.动态相对静态路由来说&#xff0c;找路更灵活&#xff0c;相互传递 2.分类&#xff1a; 二.RIP协议简介 1.三个版本RIPV1和RIPV2 (在IPV4中使用)&#xff0c; RIPNG (在IPV6中使用) 2.原理简介 三.配置 1.启用Rip 并指定进程ID (进程ID 只具有本地意义…

即插即用! | 国防科大联合慕尼黑工业大学提出新型解耦头 TSCODE: 引入yolov5/yolov7助力目标检测器轻松涨点!

1.Task-Specific COntext DEcoupling, TSCODE 介绍 论文:https://arxiv.org/pdf/2303.01047.pdf 本文提出了一种新颖的即插即用的特定于任务的上下文解耦头(Task-Specific COntext DEcoupling, TSCODE),通过进一步解开两个任务的特征编码来提升网络整体的性能 TSCODE整体的…

SYSU程设c++(第九周)函数对象、友元函数、友元类

函数对象&#xff1a; 如果一个类定义了operator()运算符函数&#xff0c;则可以使用该类的对象名为函数名调用这个函数. 函数对象是一个对象&#xff0c;但调用形式和普通函数调用一样&#xff0c;因此取名叫函数对象 (注意operator()先有个括号&#xff0c;接着才是括号(参数…

XHR 和 AJAX 的结合 - API 测试

大家好&#xff0c;之前一期介绍了怎样通过工具类进行对API 接口测试&#xff0c;这一期将演示如何手写一个 Ajax的请求。 什么是 XHR ? 全称为 XMLHttpRequest &#xff0c;它是浏览器内置的对象&#xff0c;使得 JavaScript 可以发送 HTTP 请求。 什么是Ajax ? Ajax是一种用…

Ubuntu用户权限、查看文件路径、防火墙

一、Ubuntu用户权限 1、设置root用户密码 sudo passwd root 新的 密码&#xff1a; 重新输入新的 密码&#xff1a; passwd&#xff1a;已成功更新密码2、普通用户赋予root权限 修改 /etc/sudoers 文件 su root vim /etc/sudoers把用户加入到root组 # root用户下操作 # -…

Elasticsearch 整合机器学习强化排序

作者&#xff1a;彭晟&#xff0c;2023 年 Elastic 开发者大会讲师 概述 Elasticsearch 整合机器学习强化排序, 介绍如何将机器学习预测能力迁移至 ES 内部&#xff0c;增强排序能力, 构建一个高性能&#xff0c;分布式搜排一体系统&#xff0c;并通过落地更多复杂模型特征和更…

[操作系统安全]缓冲区溢出

一、C栈帧结构 函数调用内存中的三个区域&#xff0c;代码区、静态数据区、动态数据区&#xff08;压栈和清栈就是在这个区域完成的&#xff09;。CPU中有三个寄存器&#xff0c;分别是eip、ebp和esp。eip永远指向代码区中将要执行的下一条指令&#xff0c;执行方式包括顺序执行…

NumberPicker分析(一)

NumberPicker分析(一) NumberPicker可实现连续滚动的字符串选择&#xff0c;其实现方式很有借鉴的意义 以最基本的使用方式为例&#xff0c;在layout中布局&#xff1a; <NumberPickerandroid:id"id/number_picker"android:layout_width"wrap_content"…

Visual Studio 2019 C# 上位机入门(1):制作一个简单应用

Visual Studio 2019下载安装步骤可以看&#xff1a;https://blog.csdn.net/weixin_44788542/article/details/114271126 这里不赘述&#xff0c;默认电脑上已经安装好了。 1、打开安装好的Visual Studio后&#xff0c;选择创建新项目。 2、找到选择C#下面的Windows 窗体应用&…

PCIe Protocol Basics

目录 1、PCIe Layered Architecture 2、Packet Movement 3、Simplified Layer Model 4、Layers and Packedt Generation 5、Detailed Layer Model 6、Transaction Layer 7、TransactionLayer Packet 8、TLP Header Overview 9、Data Link Layer 10、Data Link Layer Pa…

Vue 样式绑定

文章目录 Vue 样式绑定Vue classclass 属性绑定数组语法 Vue.js style(内联样式) Vue 样式绑定 Vue class class 与 style 是 HTML 元素的属性&#xff0c;用于设置元素的样式&#xff0c;我们可以用 v-bind 来设置样式属性。 Vue.js v-bind 在处理 class 和 style 时&#x…

php+vue+mysql医院医护人员医生排班系统

本医护人员排班系统管理员&#xff0c;医护。管理员功能有个人中心&#xff0c;医院信息管理&#xff0c;医护信息管理&#xff0c;医护类型管理&#xff0c;排班信息管理&#xff0c;排班类型管理&#xff0c;科室信息管理&#xff0c;投诉信息管理。医护人员可以修改自己的个…

Unity WebGL监听是否进入全屏模式

今天遇到一个需求打包成WebGL之后要当做一个iframe&#xff0c;嵌入到别的网页中&#xff0c;其中遇到两个难题。 1.要增加一个全屏模式。 2.全屏的时候使用unity中的title&#xff0c;非全屏的时候要使用网页本身的title。 全屏一开始使用webkitRequestFullScreen&#xff…

python+vue 家庭理财管理系统

本论文对家庭理财管理系统的发展背景进行详细的介绍&#xff0c;并且对系统开发技术进行介绍&#xff0c;然后对系统进行需求分析&#xff0c;对家庭理财管理系统业务流程、系统结构以及数据都进行详细说明。 1.系统功能完整性&#xff1a;根据系统每一个功能模块&#xff0c;都…

维度云工业品进销存ERP解决行业6大销售痛点

01 销售了多少?成本毛利多少? 如果不使用ERP软件进行管理&#xff0c;则需要手动记录和计算销售额和成本&#xff0c;并根据这些数据手动计算毛利润。这种方法可能会导致错误和时间浪费&#xff0c;并且很难应对规模扩大的情况。因此&#xff0c;通常建议企业使用专业的管理…

jenkins安装(Linux)

文章目录 请谨慎安装最新版本的jenkins1. Jenkins 介绍1.1 jenkins使用场景 2.jenkins下载2.1上传至Linux2.2 rpm安装jenkins2.3 修改jenkins配置2.3.1 修改内容 2.4 开放端口2.5 启动jenkins2.5.1 启动错误2.5.2 添加JAVA_HOME 2.6 jenkins配置添加自定义安装java目录2.7 Erro…

leetCode算法第一天

今天开始刷算法题&#xff0c;提升自己的算法思维和代码能力&#xff0c;加油&#xff01; 文章目录 无重复字符的最长子串最长回文子串N形变换字符串转换整数 无重复字符的最长子串 leetCode链接 https://leetcode.cn/problems/longest-substring-without-repeating-characte…