3. 四则运算

题目描述

输入一个表达式(用字符串表示),求这个表达式的值。
保证字符串中的有效字符包括[‘0’-‘9’],‘+’,‘-’, ‘*’,‘/’ ,‘(’, ‘)’,‘[’, ‘]’,‘{’ ,‘}’。且表达式一定合法。

题解

方法一:栈
import java.util.*;

public class Main {
    public static void main(String[] args) {

        Scanner sc = new Scanner(System.in);
        while (sc.hasNext()){
            char[] array = sc.nextLine().toCharArray();
            System.out.println(calcular(array));
        }

    }

    public static int calcular(char[] array){
        Map<Character, Character> map = new HashMap<>();
        map.put('(', ')');
        map.put('[',']');
        map.put('{', '}');
        Stack<Character> stack = new Stack<>();

        for (int i = array.length-1; i >=0; i--){
            char c = array[i];
            if (stack.empty()){
                stack.push(c);
            } else{
               if (map.containsKey(c)){
                   // 循环取出栈中的数据,直到匹配value为止
                   String s = getSubInBrackets(map, stack, c);
                   String calculate = getAndCalculate(s);
                   for (int k = calculate.length()-1; k >=0; k--){
                       stack.push(calculate.charAt(k));
                   }
               } else {
                   stack.push(c);
               }
            }
        }

        // 取出栈中剩余的数据
        StringBuilder sb = new StringBuilder();
        while(true){
            if (stack.empty()){
                break;
            }
            sb.append(stack.pop());
        }
        String re = getAndCalculate(sb.toString());

        return Integer.parseInt(re);

    }

    private static String getAndCalculate(String sb) {
        // 取出的字符长度小于3,则直接当做一个整体的数字(一定是负数)放入栈中
        if (sb.length() < 3){
            return sb;
        }
        // 两个符号之间的数为要计算的数,作为整体; 负号与数字一起作为一个整体,使整个运算中只有加法、乘法、除法
        List<String> numList = handleMinus(sb);

        int sum = 0;
        int lastNum = 0;  // 上一个数,可能为计算后的数
        List<String> add = new ArrayList<>();
        for (int j = 0; j < numList.size(); ){
            int xj = 0;
            if (numList.get(j).equals("*")) {
                sum -= lastNum;  // 减去上一个已加的数
                xj = lastNum * Integer.parseInt(numList.get(j+1)); // 上个数*下一个数
                lastNum = xj; // 乘积作为下个运算的上一个数
                sum += xj; // 加上积
                j += 2;
            } else if (numList.get(j).equals("/")){
                sum -= lastNum;  // 减去上一个已加的数
                xj = lastNum / Integer.parseInt(numList.get(j+1)); // 上一个数/下一个数
                lastNum = xj; // 运算结果作为下一个运算的上一个数
                sum += xj; // 加上运算结果
                j += 2;
            } else {
                sum += Integer.parseInt(numList.get(j)); // 加上当前数
                lastNum = Integer.parseInt(numList.get(j));
                j++;
            }
        }
        return String.valueOf(sum);
    }

    /**
     * 处理减号和负号,分割数字和符号,最终只剩下数字,*,/
     * 如例子:8-6  变成 [8, -6]
     * @param sb
     * @return
     */
    private static List<String> handleMinus(String sb) {
        int start = 0;
        int end = 0;
        List<String> numList = new ArrayList<>();
        for (int i = 0; i < sb.length(); i++){
            char sign = sb.charAt(i);
            if (sign == '+') {
                numList.add(sb.substring(start,end));
                start = end = i+1;
            } else if (sign == '*' || sign == '/'){
                numList.add(sb.substring(start,end));
                numList.add(String.valueOf(sign));
                start = end = i+1;
            } else if (sign == '-'){
                if(start < end){
                    numList.add(sb.substring(start,end));
                }
                start = end = i;
                end += 1;
            } else{
                end++;
            }
        }
        if (end > start) {
            numList.add(sb.substring(start));
        }
        return numList;
    }

    /**
     * 获取括号内的字符串
     *
     * @param map
     * @param stack
     * @param c
     * @return
     */
    private static String getSubInBrackets(Map<Character, Character> map, Stack<Character> stack, char c) {
        StringBuilder sb = new StringBuilder();
        while(true){
            Character pop = stack.pop();
            if (pop.equals(map.get(c))){
                break;
            }
            sb.append(pop);
        }
        return sb.toString();
    }
}





要点/思路:

先使用栈取出优先级最高的式子;
如果取出来的字符串长度小于3,则一定是负数,重新压入栈中;
如果取出来的字符串长度大于3,将字符串按照’+‘,’-‘,’’,'/‘拆分成有序的字符串列表,使列表中只包含数字(负号变成负数)、’‘、’/';然后对列表进行运算;
—运算规则:从前往后做加法,遇到乘法(除法),则减去上一个加的数,并且使用上个数乘以(除以)下一个数,然后加上乘积(商)
将运算的结果重新压入栈,继续下一次运算;
如此递归运算,得到最终结果。
如:3+2*{1+2*[-4/(8-6)+7]}

先取出: 8-6 变成 [8, -6] 计算得 2,压入栈
再取出:-4/2+7 变成 [-4, /, 2, 7] 计算得 5,压入栈
再取出:1+2*5 变成 [1, 2, , 5]计算得 11,压入栈
接着取出:3+2
11 变成 [3, 2, *, 11]计算得 25
最终结果:25

注释

1. Map()和Collection()
Map的主要特点是键值对的形式,一一对应,且一个key(不可重复)只对应一个value(可重复),定义了双列集合的规范;
Collection则每次存储一个元素,定义了单列集合的规范。
侵删

2. 通过StringBulider取出栈中剩余数据
StringBuilder类提供将各种数据类型变量的字符串形式追加到当前序列中的append方法。

3. Stack.peek()和Stack.pop()的区别
Stack.peek()
peek()函数返回栈顶的元素,但不弹出该栈顶元素。
Stack.pop()
pop()函数返回栈顶的元素,并且将该栈顶元素出栈。

4. Integer.parseInt(String)类型转换
Integer.parseInt(String)的作用就是将String字符类型数据转换为Integer整型数据。


方法二:调包暴力求解
import java.util.*;
import javax.script.*;

public class Main{
    public static void main(String[] args) throws ScriptException {
        Scanner scan = new  Scanner(System.in);
        String input = scan.nextLine();
        input = input.replace("[","(");
        input = input.replace("{","(");
        input = input.replace("}",")");
        input = input.replace("]",")");
        ScriptEngine scriptEngine = new ScriptEngineManager().getEngineByName("nashorn");
        System.out.println(scriptEngine.eval(input));
    }
}

注释

1. 使用Java Scripting API的步骤

  1. 创建一个 ScriptEngineManager 对象.
  2. 从管理器对象中获取 ScriptEngine 对象
  3. 使用脚本引擎的eval() 方法来执行脚本

2. JavaScript replace() 方法
replace() 方法用于在字符串中用一些字符替换另一些字符,或替换一个与正则表达式匹配的子串。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值