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

这篇具有很好参考价值的文章主要介绍了Java LeetCode篇-深入了解关于栈的经典解法(栈实现:中缀表达式转后缀)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

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

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

文章目录

        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() 方法进行比较,不然会发生问题。

Java LeetCode篇-深入了解关于栈的经典解法(栈实现:中缀表达式转后缀),Java LeetCode篇,java,算法,leetcode,链表文章来源地址https://www.toymoban.com/news/detail-757901.html

到了这里,关于Java LeetCode篇-深入了解关于栈的经典解法(栈实现:中缀表达式转后缀)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java LeetCode篇-二叉树经典解法(实现:判断平衡二叉树、找两个节点最近的祖先等)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍     文章目录         1.0 平衡二叉树         1.1 实现判断平衡二叉树的思路         1.2 代码实现判断平衡二叉树         2.0 二叉树的层序遍历         2.1 实现二叉树层序遍历的思路          2.2

    2024年02月05日
    浏览(35)
  • Java LeetCode篇-二叉搜索树经典解法(实现:二叉搜索树的最近公共祖先、根据前序遍历建树等)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍    文章目录         1.0 判断合法         1.1 使用遍历方式实现验证二叉搜索树         1.2 使用递归方式实现验证二叉搜索树         2.0 求范围和         2.1 使用非递归实现二叉搜索树的范围和

    2024年02月03日
    浏览(37)
  • Java 算法篇-深入了解单链表的反转(实现:用 5 种方式来具体实现)

    🔥博客主页:  小扳_-CSDN博客 ❤感谢大家点赞👍收藏⭐评论✍          文章目录         1.0 单链表的反转说明         2.0 单链表的创建         3.0 实现单链表反转的五种方法         3.1 实现单链表反转 - 循环复制(迭代法)         3.2 实现单链表反转 - 头插法

    2024年02月05日
    浏览(30)
  • 《深入理解Java虚拟机》读书笔记:基于栈的字节码解释执行引擎

      虚拟机是如何调用方法的内容已经讲解完毕,从本节开始,我们来探讨虚拟机是如何执行方法中的字节码指令的。上文中提到过,许多Java虚拟机的执行引擎在执行Java代码的时候都有 解释执行(通过解释器执行) 和 编译执行(通过即时编译器产生本地代码执行) 两种选

    2024年02月11日
    浏览(38)
  • JavaEE 初阶篇-深入了解单例模式(经典单例模式:饿汉模式、懒汉模式)

    🔥博客主页: 【 小扳_-CSDN博客】 ❤感谢大家点赞👍收藏⭐评论✍ 文章目录         1.0 单例模式的概述         2.0 单例模式 - 饿汉式单例         2.1 关于饿汉式单例的线程安全问题         3.0 单例模式 - 懒汉式单例         3.1 关于懒汉式单例的线程安全问题      

    2024年04月15日
    浏览(34)
  • 【经典算法】LeetCode 5: 最长回文子串(Java/C/Python3实现含注释说明,Medium)

    标签(题目类型):回文串、动态规划 原题:LeetCode 5 思路 Dynamic Programming(DP) 动态规划是一种将问题分解成子问题并分别计算的优化技术。对于回文子串,我们可以使用动态规划来解决。 对于一个子串而言,如果它是回文串,并且长度大于 2,那么将它首尾的两个字母去除之后

    2024年04月14日
    浏览(54)
  • 【经典算法】 leetcode88.合并排序的数组(Java/C/Python3实现含注释说明,Easy)

    作者主页: 🔗进朱者赤的博客 精选专栏:🔗经典算法 作者简介:阿里非典型程序员一枚 ,记录在大厂的打怪升级之路。 一起学习Java、大数据、数据结构算法( 公众号同名 ) ❤️觉得文章还不错的话欢迎大家点赞👍➕收藏⭐️➕评论,💬支持博主,记得点个大大的 关

    2024年04月22日
    浏览(27)
  • Java中的单点登录原理与实现方案探究:深入了解安全与便捷的用户认证解决方案

    目录 1、什么是单点登录 2、单点登录的优势和应用场景 3、单点登录的原理和实现方式 3.1 传统的Cookie和Session实现方式 3.2 基于Token的实现方式 3.3 基于OAuth2的实现方式 4、单点登录的技术要点和关键问题 4.1 安全性考虑 4.2 用户体验优化 4.3 高可用性设计 5、Java中的单点登录实

    2024年01月23日
    浏览(46)
  • Go-Python-Java-C-LeetCode高分解法-第八周合集

    本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接:LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏,每日一题,和博主一起进步 LeetCode专栏 我搜集到了50道精选题,适合速成概览大部分常用算法 突破算法迷宫:精选50道-算法刷题指南 本文部分内容来自网上

    2024年02月07日
    浏览(36)
  • Go-Python-Java-C-LeetCode高分解法-第十二周合集

    本题解Go语言部分基于 LeetCode-Go 其他部分基于本人实践学习 个人题解GitHub连接:LeetCode-Go-Python-Java-C 欢迎订阅CSDN专栏,每日一题,和博主一起进步 LeetCode专栏 我搜集到了50道精选题,适合速成概览大部分常用算法 突破算法迷宫:精选50道-算法刷题指南 Given a set of distinct int

    2024年02月08日
    浏览(35)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包