letcode 超级计算器

本文介绍了一种基于逆序波兰表达式的计算器实现方法,通过使用两个栈——数字栈和操作符栈,来解析和计算复杂的算术表达式。文章详细分析了计算过程,并提供了具体的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

letcode 16.26. 计算器。
给定一个包含正整数、加(+)、减(-)、乘(*)、除(/)的算数表达式(括号除外),计算其结果。

表达式仅包含非负整数,+, - ,*,/ ,(,)四种运算符和空格 。 整数除法仅保留整数部分。

示例 1:

输入: "3+2*2"
输出: 7

示例 2:

输入: " 3/2 "
输出: 1

示例 3:

输入: " 3+5 / 2 "
输出: 5

说明:

你可以假设所给定的表达式都是有效的。
请不要使用内置的库函数 eval。

分析一个复杂的过程:

// 超级计算器:利用两个栈,一个数字栈,一个操作栈。过程利用逆序波兰表达式。
str="12*(2+(3+(4+2*(5+6))))$"; $为结束符号  看看如何做入栈出栈操作的。
用sb存上次的操作数字,这是为了解决大数字比如22  nowChar存当前字符 len为sb的length
详细分析:
i=0: 1 是数字 暂时存sb='1' nowChar='1'
i=1: 2 是数字 暂时存sb='12' nowChar='2'
i=2: * 不是数字 len>0 将sb入栈numStack[12.0], len=0,* 入栈operStack[*] nowChar='*'
i=3: ( 不是数字 len=0 sb不入栈,( 入栈operStack[*,(] nowChar='('
i=4: 2 是数字 暂时存sb='2' nowChar='2'
i=5: + 不是数字 len>0 将sb入栈numStack[12.0,2.0], len=0,+ 入栈operStack[*,(,+] nowChar='+'
i=6: ( 不是数字 len=0 sb不入栈,( 入栈operStack[*,(,+,(] nowChar='('
i=7: 3 是数字 暂时存sb='3' nowChar='3'
i=8: + 不是数字 len>0 将sb入栈numStack[12.0,2.0,3.0], len=0,+ 入栈operStack[*,(,+,(,+] nowChar='+'
i=9: ( 不是数字 len=0 sb不入栈,( 入栈operStack[*,(,+,(,+,(] nowChar='('
i=10: 4 是数字 暂时存sb='4' nowChar='4'
i=11: + 不是数字 len>0 将sb入栈numStack[12.0,2.0,3.0,4.0], len=0,+ 入栈operStack[*,(,+,(,+,(,+] nowChar='+'
i=12: 2 是数字 暂时存sb='2' nowChar='2'
i=13: * 不是数字 len>0 将sb入栈numStack[12.0,2.0,3.0,4.0,2.0], len=0,* 入栈operStack[*,(,+,(,+,(,+,*] nowChar='*'
i=14: ( 不是数字 len=0 sb不入栈,( 入栈operStack[*,(,+,(,+,(,+,*,(] nowChar='('
i=15: 5 是数字 暂时存sb='5' nowChar='5'
i=16: + 不是数字 len>0 将sb入栈numStack[12.0,2.0,3.0,4.0,2.0,5.0], len=0,+ 入栈operStack[*,(,+,(,+,(,+,*,(,+] nowChar='+'
i=17: 6 是数字 暂时存sb='6' nowChar='6'
i=16: ) 不是数字 len>0 将sb入栈numStack[12.0,2.0,3.0,4.0,2.0,5.0,6.0], len=0,
	) 解栈标记 nowChar=')' 计算括号内的内容,直到找到成对'(':
	首先:operStack出栈'+' 不是'('。那么根据数据栈弹出两个数据和当前操作符计算,5.0+6.0=11.0
	并放入数据栈numStack[12.0,2.0,3.0,4.0,2.0,11.0],operStack[*,(,+,(,+,(,+,*,(].继续找
	operStack出栈'(' 组成一对,'('弹出并break出当前循环operStack[*,(,+,(,+,(,+,*]
i=17: ) 不是数字 len=0 sb不入栈
	) 解栈标记 nowChar=')' 计算括号内的内容,直到找到成对'(':
	首先:operStack出栈'*' 不是'('。那么根据数据栈弹出两个数据和当前操作符计算,2.0+11.0=22.0
	并放入数据栈numStack[12.0,2.0,3.0,4.0,22.0],operStack[*,(,+,(,+,(,+].继续找
	operStack出栈'+' 不是'(' 根据数据栈弹出两个数据和当前操作符计算,4.0+22.0=26.0
	入栈numStack[12.0,2.0,3.0,26.0] operStack[*,(,+,(,+,(]。继续。
	出栈'('组成一对,'('弹出并break出当前循环operStack[*,(,+,(,+]
i=18到i=21 重复上面i7的过程得到numStack[12.0,31.0] operStack[*]
i=22 $ 为结束符号 len=0.说明上面入栈出栈完成。还需要查栈中是否操作完成以及len不为0的情况。栈不为空,继续计算
	12.0*31.0=372.0 numStack[372.0] operStack[]
到此计算完成。

简单优先级计算分析:

// 超级计算器:利用两个栈,一个数字栈,一个操作栈。过程利用逆序波兰表达式。
str="3*4+1$"; $为结束符号  看看如何做入栈出栈操作的。
用sb存上次的操作数字,这是为了解决大数字比如22  nowChar存当前字符 len为sb的length
再来分析普通的情况:
i=0: numStack[] operStack[]
i=1: numStack[3] operStack[*]
i=2: numStack[3,4] operStack[*]
i=3: numStack[3,4,1] operStack[*,+]	
i=4: $结束符号 numStack[3,4,1] operStack[*,+]
计算过程:(1+4)*3显然不对。* /的优先级应该大于+ -
所以要做下一次计算操作来前,operStack拿操作栈的栈顶符,是高优先级则计算。
i=3: + 是计算+/-符号 operStack[*]弹出是* /高优先级计算,则先计算3*4=12,入栈numStack[12].
再继续。

实际代码:

package com.jvm.stu;


import java.util.Stack;
import java.util.regex.Pattern;

public class Main {

    static final Pattern EXGRE_NUM = Pattern.compile("^[0-9\\\\.]$");
    public static void main(String[] args) {
        // 为了方便计算我增加了一个$表示结束
        System.out.println(calculate("3*4+2$"));
        // System.out.println(calculate("2*(2+(3+(4+2*(5+6))))$"));
    }
    // 贴近计算改为返回double
    public static double calculate(String s) {
        Stack<Double> numStack= new Stack<>();
        Stack<Character> operStack = new Stack<>();
        // 用于处理123,0.123的情况
        StringBuilder sb = new StringBuilder();
        char nowChar = '$';
        for (int i = 0; i < s.length(); ++i) {
            char ch = s.charAt(i);
            if (isNum(ch)) {
                sb.append(ch);
            } else if (isSymbol(ch)) {
                if (sb.length() > 0) {
                    numStack.push(Double.valueOf(sb.toString()));
                    sb.setLength(0);
                }
                // + - 处理 比如情况 1+(-2)
                if (isSign(ch) && ('(' == nowChar || '$' == nowChar || isOper(nowChar))) {
                    sb.append(ch);
                } else if (')' == ch) {
                    while (!operStack.isEmpty()) {
                        if (operStack.peek() == '(') {
                            operStack.pop();
                            break;
                        }
                        calcu(numStack, operStack);
                    }
                } else {
                    if (!operStack.isEmpty() && isSign(ch) && isHighOper(operStack.peek())) {
                        // 优先级高先计算
                        calcu(numStack, operStack);
                    }
                    operStack.push(ch);
                }
            }
            nowChar = ch;
        }
        // 检查计算完全
        if (sb.length() > 0) {
            numStack.push(Double.valueOf(sb.toString()));
        }
        while (!operStack.isEmpty()) {
            calcu(numStack, operStack);
        }
        return numStack.pop();
    }

    private static void calcu(Stack<Double> numStack, Stack<Character> operStack) {
        Double num1 = Double.valueOf(numStack.pop());
        // 当前弹出,为空栈
        if (numStack.isEmpty()) {
            numStack.push(num1);
        }
        Double num2 = Double.valueOf(numStack.pop());
        switch (operStack.pop()) {
            case '+': numStack.push(num2 + num1); break;
            case '-': numStack.push(num2 - num1); break;
            case '*': numStack.push(num2 * num1); break;
            case '/': numStack.push(num2 / num1); break;
            case '(': operStack.push('('); return;
            default:
                break;
        }
    }


    // 数字
    public static boolean isNum(char ch){
        return EXGRE_NUM.matcher(ch + "").find();
    }
    // 优先级
    public static boolean isHighOper(char ch){
        return ch == '*' || ch == '/';
    }
    // 正负
    public static boolean isSign(char ch){
        return ch == '-' || ch == '+';
    }
    // 操作符
    public static boolean isOper(char ch){
        return isSign(ch) || isHighOper(ch);
    }
    public static boolean isSymbol(char ch){
        return isOper(ch) || '(' == ch || ')' == ch;
    }
}

这里存在优化的点:凡是能用stack实现的都能用linkedList实现,后者效率更高。不累述。只是将两个操作栈换了就行。实际算法中能不用Stack就不用,最好都用linkedList。

计算:34+2$
Stack下运行时间:
在这里插入图片描述
linkedList下运行时间:
在这里插入图片描述
复杂计算:2
(2+(3+(4+2*(5+6))))$
Stack下运行时间:
在这里插入图片描述
linkedList下运行时间:
在这里插入图片描述
从上面可以看到,简单计算Stack快,复杂计算linkedList。优化从来不是一概而论,具体场景具体优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值