Java LeetCode篇-深入了解关于栈的经典解法(栈实现:中缀表达式转后缀)

news2025/1/23 7:16:54

🔥博客主页: 【小扳_-CSDN博客】
❤感谢大家点赞👍收藏⭐评论✍
   

文章目录

        1.0 中缀表达式转后缀说明

        1.1 实现中缀表达式转后缀思路

        2.0 逆波兰表达式求值

        2.1 实现逆波兰表达式求值思路

        3.0 有效的括号

        3.1 实现有效的括号思路

        4.0 栈的压入、弹出序列

        4.1 实现栈的压入、弹出序列思路

        5.0 最小栈

        5.1 实现最小栈思路


        1.0 中缀表达式转后缀说明

        中缀表达式转后缀表达式是一种常见的算术表达式转换方法,它将中缀表达式(即常见的人类习惯的表达方式,例如("3 + 4 * 2")转换为后缀表达式(也称为逆波兰表达式,例如 " 3 4 2 * +")。中缀表达式转后缀表达式的转换过程通常使用栈来实现

        1.1 实现中缀表达式转后缀思路

        1.1.1 思路具体为:首先先来讨论优先级问题,分两种情况。

        情况一:不带括号,对于 " + " " - " " * " " / ",这个四种运算符来定义优先级大小,"+" "-" 优先级大小可以设置为 1," * " " / " 优先级大小可以设置为 2 。

        情况二:带括号,对于 " ( "  来说,将其优先级设置为 0 ,为最小的优先级。

        再来讨论代码实现的流程:对于运算符或者括号来说,将其放到栈中暂时存储;对于数字 0 ~ 9 来说,将其拼接到可变字符串中。

        1.1.2 接下来循环遍历字符串

        (1) 遇到非运算符,直接拼串

        (2) 遇到 + - * /

                - 它的优先级比栈顶运算符高,入栈,如:栈中是 + ,当前是 * 

                - 否则把栈里面的优先级 >= 它的都出栈,它再进栈,如栈中是 +* ,当前是 -

        (3) 遍历完成,栈里面剩余运算符依次出栈拼接到字符串中

        (4) 带()

                - 遇到左括号直接入栈,左括号优先级设置为 0 。

                - 遇到右括号就把栈里面到左括号为止的所有运算符都出栈

代码如下:

import java.util.Stack;

public class TurnToSuffix {
    public static void main(String[] args) {
        String s = "(a+b)*c";
        System.out.println(turnSuffix(s));

        String s1 = "(a+b*c-d)*e";
        System.out.println(turnSuffix(s1));

        String s2 = "a*(b+c)";
        System.out.println(turnSuffix(s2));
    }

    public static String turnSuffix(String s) {
        if (s.length() == 0) {
            return null;
        }

        StringBuilder str = new StringBuilder();
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {
            char ch = s.charAt(i);

            switch (ch) {
                case '(' -> {
                    //如果是有括号,直接进栈
                    stack.push(ch);
                }
                case '+', '-','*','/' -> {
                    //比较优先级,进栈的优先级高,不需要弹出;进栈的优先级底或者同一级别,需要弹出。
                    while ( !stack.isEmpty() && priority(ch) <= priority(stack.peek())) {
                        str.append(stack.pop());
                    }
                    stack.push(ch);

                }
                case ')' -> {
                    while ( !stack.isEmpty() && stack.peek() != '(') {
                        str.append(stack.pop());
                    }
                    stack.pop();
                }
                default -> {
                    str.append(ch);
                }
            }

        }
        int num = stack.size();
        for (int j = 0; j < num; j++) {
            str.append(stack.pop());
        }
        return str.toString();
    }

    public static int priority(char f) {
        if (f == '(') {
            return 0;
        } else if (f == '+' || f == '-') {
            return 1;
        }else if (f == '*' || f == '/' ) {
            return 2;
        }
        return -1;
    }
}

        2.0 逆波兰表达式求值

         逆波兰表达式(也称为后缀表达式)是一种不需要括号来表示运算顺序的算术表达式。它的计算方式是从左到右扫描表达式,遇到操作数就将其压入栈中,遇到操作符就从栈中弹出相应数量的操作数进行运算,并将结果再次压入栈中。最终栈中的唯一元素就是表达式的值。

题目:

        给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

示例 1:

输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

        2.1 实现逆波兰表达式求值思路

        思路具体为:遍历字符串数组,将遍历遇到的非运算符都要存放在栈中;遇到运算符需要将栈中的数据弹出栈,第一个弹出的数据称为右操作数,第二个弹出来的数据称为左操作数。接着对应相应的运算符进行对该这两个数据操作,得到的计算结果需要重新压入栈中。最后循环结束,栈中存放的就是该表达式最终的结果了。

代码如下:

class Solution {
    public static int evalRPN(String[] tokens) {
        LinkedList<Integer> stack = new LinkedList<>();
        for (int i = 0; i < tokens.length; i++) {
            int a = 0;
            int b = 0;
            switch (tokens[i]) {
                case "+":
                    b = stack.pop();
                    a = stack.pop();
                    stack.push(a + b);
                    break;
                case "-":
                    b = stack.pop();
                    a = stack.pop();
                    stack.push(a - b);
                    break;
                case "*":
                    b = stack.pop();
                    a = stack.pop();
                    stack.push(a * b);
                    break;
                case "/":
                    b = stack.pop();
                    a = stack.pop();
                    stack.push(a / b);
                    break;
                default:
                    stack.push(Integer.parseInt(tokens[i]));
                    break;
            }
        }
        return stack.pop();
    }
}

        需要注意的点是:从字符串数组中得到的非运算符是,需要将其转换为 int 的包装类型。

        3.0 有效的括号

题目:

        给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

该题的 LeetCode 链接:20. 有效的括号

        3.1 实现有效的括号思路

        具体思路为:遍历字符串,每一次循环得到的字符可以大致分为两种情况:

        第一种情况:遇到的是右括号,无论是 ' ( '  ' { '  ' [ '  的其中一种,都需要将其相反的符号压入栈中。如:遇到的是 ' ( ' ,那么需要压入栈的是 ' ) '

        第二种情况:遇到的是左括号,无论是  ' ) '  ' } '  ' ] '  的其中一种,先要判断栈中是否为空,如果为空,直接返回 false ;当不为空时,若判断栈顶的括号是否跟遇到的左括号相等,如果相等,将栈顶的括号弹出,若不相等,直接返回 false

        最后循环结束了,判断栈是否为空,如果栈为空了,那么说明都一一对称,有相应的括号与之匹配;如果不为空,那么说明,不是全部的括号都要相应的括号对应。

代码如下:

class Solution {
public  static boolean isValid(String s) {
        if (s.length() == 0) {
            return false;
        }
        Stack<Character> stack = new Stack<>();
        for (int i = 0; i < s.length(); i++) {

            char ch = s.charAt(i);
            switch (ch) {
                case '[' -> {
                    stack.push(']');
                }
                case '{' -> {
                    stack.push('}');
                }
                case '(' -> {
                    stack.push(')');
                }
                default -> {
                    if (stack.empty()) {
                        return false;
                    }
                    if (ch != stack.peek()) {
                        return false;
                    } else if (ch == stack.peek()) {
                        stack.pop();
                    }

                }
            }

        }
        return stack.empty();
    }
}

        4.0 栈的压入、弹出序列

题目:

        输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。

1. 0<=pushV.length == popV.length <=1000

2. -1000<=pushV[i]<=1000

3. pushV 的所有数字均不相同

示例1

输入:

[1,2,3,4,5],[4,5,3,2,1]

返回值:

true

说明:

可以通过push(1)=>push(2)=>push(3)=>push(4)=>pop()=>push(5)=>pop()=>pop()=>pop()=>pop()
这样的顺序得到[4,5,3,2,1]这个序列,返回true      

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

        4.1 实现栈的压入、弹出序列思路

        第一个序列表示栈的压入顺序第二个序列可能为该栈的弹出顺序

        具体思路为:遍历第一个序列,每一次得到的数值都要跟第二个序列索引从 0 开始到该序列长度 - 1 比较,如果从第一个序列得到的数值跟第二个序列的数值不相同时,需要将第一序列的数值压入栈中;如果从第二个序列得到的数值跟第二个序列的数值相同时,需要将第一个序列的数值弹出栈中,接着第二个序列跳到下一个数值跟栈中的数值进行比较,若相同,栈中继续弹出,第二个序列继续跳到下一个。若不相同,继续遍历第一个序列。

        可以简单的理解为:每一次循环第一个序列得到数据都要放到栈中,接着跟第二个序列的数据进行比较。如果栈顶的数据跟第二个序列的数值相同时,需要将其栈顶元素弹出,还需要将第二个序列移到下一个元素。接着继续比较,直到栈顶元素跟第二个序列的元素不相等是,继续遍历第一个序列,直到第一个序列遍历完毕。最后判断占是否为空,若为空,返回 true ;若不为空,返回 false 。

代码如下:

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param pushV int整型一维数组 
     * @param popV int整型一维数组 
     * @return bool布尔型
     */
    public boolean IsPopOrder (int[] pushV, int[] popV) {
        // write code here
        Stack<Integer> stack = new Stack<>();
        int j = 0;
        for(int i = 0; i < pushV.length ; i++) {

            stack.push(pushV[i]);

            while( !stack.isEmpty() && j < popV.length && stack.peek() == popV[j]) {
                stack.pop();
                j++;
            }

        }
        return stack.isEmpty();

    }
}

        需要注意的是,在内层循环时,要保证栈中不为空且不能超过第二个序列的长度。

      

        5.0 最小栈

题目:

        设计一个支持 push ,pop ,top 操作,并能在常数时间内检索到最小元素的栈。

实现 MinStack 类:

  • MinStack() 初始化堆栈对象。
  • void push(int val) 将元素val推入堆栈。
  • void pop() 删除堆栈顶部的元素。
  • int top() 获取堆栈顶部的元素。
  • int getMin() 获取堆栈中的最小元素。

示例 1:

输入:
["MinStack","push","push","push","getMin","pop","top","getMin"]
[[],[-2],[0],[-3],[],[],[],[]]

输出:
[null,null,null,null,-3,null,0,-2]

解释:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin();   --> 返回 -3.
minStack.pop();
minStack.top();      --> 返回 0.
minStack.getMin();   --> 返回 -2.

该题的 LeetCode 链接为:155. 最小栈

        5.1 实现最小栈思路

        具体思路为:因为需要记录栈中的最小元素,所以需要用到两个栈,对于 stack 栈来说,就正常的压栈、出栈、查看栈顶元素等操作即可;

        压栈处理:对于 minStack 栈来说,当该栈为空时,stack 压栈时minStack 也要压栈相同的元素;当 minStack 不为空时,satck 压栈时,minStack 需要先判断该栈顶元素的值是否大于需要压入栈的元素的值,若大于等于,则 minStzck 需要将其压入栈,若小于,则不需要进行任何操作。

        出栈处理:需要先判断 stack 是否为空栈,如果是空栈,则不需要进行出栈处理。如果不为空栈,stack 需要出栈,但是对于 nimStack 不一定需要出栈,若 stack 的栈顶与 nimStack 的栈顶元素相同时,nimStack 需要出栈。若不相同,则不需要出栈。

        查看栈顶元素处理:直接返回 stack.pop() 即可。

        查看最小栈的元素:直接返回 minStack.peek() 即可。

代码如下:

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.isEmpty()) {
            minStack.push(val);
        }else if(minStack.peek() >= val) {
            minStack.push(val);
        }

    }
    
    public void pop() {
        if(stack.pop().equals(minStack.peek())){
            minStack.pop();
        }
        
    }
    
    public int top() {
        return stack.peek();

    }
    
    public int getMin() {
        return minStack.peek();

    }
}

        需要注意的是, stack minStack 的栈顶元素进行比较时,需要用 equals() 方法进行比较,不然会发生问题。

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

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

相关文章

魔法时代:人工智能如何重塑我们的日常生活

现在的我们正在目睹一个新时代的到来——人工智能(AI)的时代。这个时代由算法驱动&#xff0c;由数据精炼&#xff0c;由机器学习引领&#xff0c;正逐渐成为我们生活中不可或缺的一部分。人工智能像一位魔法师&#xff0c;以其独特的能力&#xff0c;从根本上改变了我们的生活…

力扣题:字符的统计-12.2

力扣题-12.2 [力扣刷题攻略] Re&#xff1a;从零开始的力扣刷题生活 力扣题1&#xff1a;423. 从英文中重建数字 解题思想&#xff1a;有的单词通过一个字母就可以确定&#xff0c;依次确定即可 class Solution(object):def originalDigits(self, s):""":typ…

Hdoop学习笔记(HDP)-Part.14 安装YARN+MR

目录 Part.01 关于HDP Part.02 核心组件原理 Part.03 资源规划 Part.04 基础环境配置 Part.05 Yum源配置 Part.06 安装OracleJDK Part.07 安装MySQL Part.08 部署Ambari集群 Part.09 安装OpenLDAP Part.10 创建集群 Part.11 安装Kerberos Part.12 安装HDFS Part.13 安装Ranger …

可用的镜像 yum 源

目录 ftp.sjtu.edu.cn 镜像 yum 源centos 的镜像 yum 源 mirrors.sohu.comcentos 的镜像 yum 源 mirrors.163.comcentos 的镜像 yum 源 ftp.sjtu.edu.cn 镜像 yum 源 镜像 yum 源地址 &#xff1a; http://ftp.sjtu.edu.cn/centos/ centos 的镜像 yum 源 http://ftp.sjtu.edu…

git如何关联克隆远程仓库

一、添加远程仓库 之前我们仅仅是在本地创建了一个Git本地仓库&#xff0c;这里我们再在GitHub创建一个Git远程仓库&#xff0c;并且让这两个仓库进行远程同步&#xff0c;这样&#xff0c;GitHub上的仓库既可以作为备份&#xff0c;又可以让其他人通过该仓库来协作开发。 1.…

面试--各种场景问题总结

1.在开发过程中&#xff0c;你是如何保证机票系统的正常运行的&#xff1f; 用户、测试、监控和日志、安全措施、数据备份、系统设计、需求分析 2.在机票系统开发过程中&#xff0c;你最有成就的事情&#xff0c;为什么&#xff1f; 用户体验感、高可用和稳定性、客户满意度、系…

使用Visual Studio创建第一个C代码工程

文章目录 2019创建C工程创建C文件运行 上一节我们使用记事本编辑C代码&#xff0c;在命令行运行文件&#xff0c;这种方式只是作为对编译器的了解&#xff0c;实际的开发中一般使用集成开发环境比较多&#xff0c;因为 集成开发环境操作比较简单&#xff0c;通常可编辑&#x…

cc-product-waterfall仿天猫、淘宝购物车店铺商品列表组件

cc-product-waterfall仿天猫、淘宝购物车店铺商品列表组件 引言 在电商应用中&#xff0c;购物车体验的优化对于提升用户满意度和转化率至关重要。在本文中&#xff0c;我们将深入探讨如何使用cc-product-waterfall组件&#xff0c;结合uni-number-box和xg-widget&#xff0c;…

WebGL笔记:图形缩放的原理和实现

缩放 1 &#xff09;原理 缩放可以理解为对向量长度的改变&#xff0c;或者对向量坐标分量的同步缩放 如下图&#xff0c;比如让向量OA 收缩到点B的位置&#xff0c;也就是从OA变成OB&#xff0c;缩放了一半 2 &#xff09;公式 已知 点A的位置是(ax,ay,az)点A基于原点內缩了…

L1-009:N个数求和

目录 ⭐题目描述⭐ ⭐分析 ⭐程序代码 运行结果 ⭐文案分享⭐ ⭐题目描述⭐ 本题的要求很简单&#xff0c;就是求N个数字的和。麻烦的是&#xff0c;这些数字是以有理数分子/分母的形式给出的&#xff0c;你输出的和也必须是有理数的形式。 输入格式&#xff1a; 输入第一行给出…

GAN:PacGAN-生成对抗网络中两个样本的威力

论文&#xff1a;https://arxiv.org/pdf/1712.04086.pdf 代码&#xff1a;GitHub - fjxmlzn/PacGAN: [NeurIPS 2018] [JSAIT] PacGAN: The power of two samples in generative adversarial networks 发表&#xff1a;2016 一、摘要 1&#xff1a;GAN最重大的缺陷是&#xf…

数据库管理-第121期 我为什么写文章(202301203)

数据库管理-第121期 我为什么写文章&#xff08;202301203&#xff09; 其实呢~大周末我不是太想写文章的&#xff0c;周五HaloDB起了个头还有一堆可以做的事情都计划到下周了&#xff0c;但是昨天发生了一件事情&#xff0c;让我很是不开心&#xff1a;强盗逻辑&#xff0c;白…

部署 Draw.io 思维导图工具

1&#xff09;Draw.io 介绍 提到流程图&#xff0c;大家第一时间可能会想到 Visio&#xff0c;不可否认&#xff0c;VIsio 确实是功能强大&#xff0c;但是软件为收费&#xff0c;并且因为其功能强大&#xff0c;导致安装需要很多的系统内存&#xff0c;并且是不可跨平台使用。…

分治-归并算法——LCR 170. 交易逆序对的总数

文章目录 &#x1f33c;0. 归并排序&#x1f33b;1. 题目&#x1f33c;2. 算法原理&#x1f337;3. 代码实现 &#x1f33c;0. 归并排序 归并排序是典型的分治&#xff0c;将数组分成若干个子数组&#xff0c;数组两两比较&#xff0c;不是很清楚的&#xff0c;可以查看此篇文…

VUE设计与实现共读系列之ref的实现【响应式原理】

前言 我们先顺一下vue使用响应式数据的流程&#xff1a; vue 是通过 ref 和 reactive 来创建响应式值&#xff0c;改变响应式值&#xff0c;视图跟着发生变化。 我们今天就来看一下ref和reactive是如何实现的 准备 首先&#xff0c;打开ref函数的位置 我们可以看到一个被re…

10行代码实现vue路由最简单的登陆拦截

需求&#xff1a;不涉及任何角色权限&#xff0c;基本实现目标&#xff0c;有token就可查看任何页面&#xff0c;否则就去登陆&#xff0c;来一步步实现 1. 创建你的路由页面&#xff0c;此处略了 2. 导航守卫拦截判断思路 // 创建路由 const router createRouter({history…

Python----字典练习

相关链接&#xff1a;Python---字典的增、删、改、查操作_python中字典的增删改查-CSDN博客 Python---字典---dict-CSDN博客 Python---引用变量与可变、非可变类型-CSDN博客 重点&#xff1a; 字典中的 key &#xff08;就是键&#xff09;可以是很多数据类型&#xff08;…

对比ProtoBuf和JSON的序列化和反序列化能力

1.序列化能力对比验证 在这里让我们分别使用PB与JSON的序列化与反序列化能力&#xff0c;对值完全相同的一份结构化数据进行不同次数的性能测试。 为了可读性&#xff0c;下面这一份文本使用JSON格式展示了需要被进行测试的结构化数据内容: {"age" : 20,"name…

SpringAMQP入门案例——接收消息

依赖 <!--SpringAMQP起步依赖--> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> yml配置文件 自行修改 spring:rabbitmq:host: 192.168.220.130 # …

Spring MVC学习随笔-控制器(Controller)开发详解:接受客户端(Client)请求参数

学习视频&#xff1a;孙哥说SpringMVC&#xff1a;结合Thymeleaf&#xff0c;重塑你的MVC世界&#xff01;&#xff5c;前所未有的Web开发探索之旅 第三章、SpringMVC控制器开发详解 3.1 核心要点 &#x1f4a1; 1. 接受客户端&#xff08;client&#xff09;请求参数[讲解] 2…