小白学习java栈的表达式问题——第四关黄金挑战

内容

1.理解计算器如何实现
2.理解逆波兰表达式的实现方式

目录

1.计算器问题

2 逆波兰表达式

1.计算器问题

LeetCode227链接

 解决运算器问题,最好的工具就是栈。

由于乘除优先加减计算。因此不妨考虑先进性所有乘除运算,并将这些乘除运算后的整数值放回原来表达式的相应位置,随后整个表达式的值,就等于一系列整数加减后的值。

基于此,我们可以用一个栈,保存这些(进行乘除运算后的)整数的值。对于加减号后的数字,将其直接压入栈中,对于乘除号后 的数字,可以直接与栈顶元素计算,并替换栈顶元素为计算的结果。

具体来说,遍历字符串ss,并用变量preSign记录每一个数字之前的运算符,对于第一个数字,其之前的运算符视为加号,每次遍历到数字末尾时,根据preSign来决定计算方式:

加号:将数字压入栈;

减号:将数字的相反数压入栈;

乘除号:计算数字与栈顶元素,并将栈顶元素替换为计算结果。

代码实现中,若读到一个运算符,或者遍历到字符串末尾,即认为是遍历到了数字末尾。

处理完该数字后,更新preSign为当前遍历的字符。

遍历完字符串ss后,将栈中元素累加起来。即为该字符串表达式的值。

import java.util.ArrayDeque;
import java.util.Deque;

class Solution {
    public int calculate(String s) {
        Deque<Integer> stack = new ArrayDeque<Integer>();  // 用于存储数字的栈
        char preSign = '+';  // 初始化前一个操作符为'+'
        int num = 0;  // 初始化当前数字为0
        int n = s.length();  // 获取字符串长度
        for (int i = 0; i < n; ++i) {
            if (Character.isDigit(s.charAt(i))) {  // 如果当前字符是数字
                num = num * 10 + s.charAt(i) - '0';  // 将当前数字字符转换为数字并更新num
            }
            // 如果当前字符不是数字且不是空格,或者已经到达字符串末尾
            if (!Character.isDigit(s.charAt(i)) && s.charAt(i) != ' ' || i == n - 1) {
                switch (preSign) {
                    case '+':
                        stack.push(num);  // 将num压入栈中
                        break;
                    case '-':
                        stack.push(-num);  // 将-num压入栈中
                        break;
                    case '*':
                        stack.push(stack.pop() * num);  // 将栈顶元素与num相乘并将结果压入栈中
                        break;
                    default:
                        stack.push(stack.pop() / num);  // 将栈顶元素与num相除并将结果压入栈中
                }
                preSign = s.charAt(i);  // 更新前一个操作符
                num = 0;  // 重置当前数字
            }
        }
        int ans = 0;  // 初始化最终结果为0
        while (!stack.isEmpty()) {
            ans += stack.pop();  // 将栈中所有数字相加
        }
        return ans;  // 返回最终结果
    }
}

2 逆波兰表达式

本道题目初次接触略有难度,如果学了二叉树后再来学可能会更好理解

表达式计算就是编译原理、自然语言处理、文本分析等领域非常重要的问题。

这里看一个相对中等的问题,逆波兰表达式。

LeetCode150链接

 本道题看起来很复杂,但是其实很简单,我们先理解一下什么是表达式,表达式就是小学里类似((2+1)*3)这样的狮子,根据不同的技法,有前缀、中缀和后缀三种方式,其区别就是在于运算符相对于操作数的位置,前缀表达式的运算符位与操作数之前,中缀和后缀同理,如下图,其实就对应了树的前中后三种遍历。

对应的三种表达式就是: 

中缀表达式:1 +( 2 + 3 )* 4 - 5
前缀表达式:- + 1 * 2 3 4 5
后缀表达式:1 2 3 + 4 * + 5 -

 通过上面的例子我们也看到了中缀表达式是最像人话的,它是一种通用的算术或逻辑公式表示方法,操作符以中缀形式处于操作数的中间。虽然人的大脑很容易理解与分析中缀表达式,但是对计算机来说中缀表达式确实很复杂的,因此计算表达式的值时,通常需要将中缀表达式转换为前缀或者后缀表达式再进行求职。

前缀表达式的运算符位与两个相应操作数之前,前缀表达式又被称为前缀记法或者波兰式

当然,后缀式就是逆波兰式。

如果用栈来解释就是遇见数字即进栈,遇见运算符,则取出栈中最上面的两个元素进行计算,最后将运算结果入栈。

代码如下:

public class EvalRPN {
    public static int evalRPN(String[] tokens) {
        Stack<Integer> stack = new Stack<>();
        for (String token : tokens) {
            if (!Character.isDigit(token.charAt(0)) && token.length() == 1) {
                /**
                 * 运算符,从栈中取出两个数进行运算!
                 */
                int b = stack.pop();
                int a = stack.pop();
                switch (token) {
                    /**
                     * 根据运算符的种类进行计算
                     * 将结果直接入栈!
                     */
                    case "+":
                        stack.push(a + b);
                        break;
                    case "-":
                        stack.push(a - b);
                        break;
                    case "*":
                        stack.push(a * b);
                        break;
                    case "/":
                        stack.push(a / b);
                        break;
                }
            } else {
                /**
                 * 整数直接入栈!
                 */
                stack.push(Integer.parseInt(token));
            }
        }
        return stack.pop();
    }

也可以的方法: 

class Solution {
    public int evalRPN(String[] tokens) {
        // 创建一个数组来存储数字,数组长度为运算符数量加 1
        int[] eval = new int[tokens.length / 2 + 1];
        int i = 0; // 用于记录当前数字在数组中的位置
        for (String token : tokens) {
            if (!(token.equals("+") || token.equals("-") || token.equals("*") || token.equals("/"))) {
                // 如果是数字,将其转换为整数并放入数组中
                eval[i++] = Integer.parseInt(token);
            } else if (token.equals("+")) {
                // 如果是加号,则将前两个数字相加,结果存入数组中,并移动指针
                eval[i - 2] = eval[i - 2] + eval[--i];
            } else if (token.equals("-")) {
                // 如果是减号,则将前两个数字相减,结果存入数组中,并移动指针
                eval[i - 2] = eval[i - 2] - eval[--i];
            } else if (token.equals("*")) {
                // 如果是乘号,则将前两个数字相乘,结果存入数组中,并移动指针
                eval[i - 2] = eval[i - 2] * eval[--i];
            } else {
                // 如果是除号,则将前两个数字相除,结果存入数组中,并移动指针
                eval[i - 2] = eval[i - 2] / eval[--i];
            }
        }

        return eval[0]; // 返回数组中唯一的元素,即计算结果
    }
}

完成✅

  • 8
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值