四则运算(逆波兰表达式)

1.四则运算

输入一个表达式(用字符串表示),求这个表达式的值。

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

示例:

​ 输入: 3+2{1+2[-4/(8-6)+7]}

​ 输出: 25

解题思路: 逆波兰表达式

   1)初始化两个栈,运算符栈s1, 数据栈s2 (将此栈定义为集合)
   2)从左到右扫描中缀表达式
   3)遇到操作数,将其压入s2
   4)遇到用算符,比较其与s1栈顶元素的优先级:
       1.如果s1为空,或者栈顶运算符为 "( || [ || {",直接压入栈中
       2.否则,优先级比栈顶元素优先级高也压入s1中,
       3.否则,将s1中用算符弹出压入s2,再转到 4.1中执行
   5) 遇到括号时:
       如果为左括号:"( || [ || {",直接压入s1
       如果为右括号: ") || ] || }", 依次弹出s1中运算符,并压入s2,直到遇到对应的左括号为止,此时丢弃这对括号
   6) 重复步骤2至5,直到表达式最右边
   7)将s1中剩余的运算符依次弹出压入s2
   8) s2即为后缀表达式
 
负数的处理:
   出现负数有两种情况: 
      ①: 表达式第一个就为 '-' 号,则判定此数为负数 如:-3+1
      ②: 表达式中'-'的前一个符号为左括号'(',则判定 此数为负数 如: (-3+1)
   转化方法在前面补0即可如: 0-3+1;   

代码:

import java.util.*;
public class Main{
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String str = sc.nextLine();
        str = str.replaceAll("[\\{\\[]", "(").replaceAll("[\\}\\]]",")"); //将中缀表达式中的括号转为小括号
        List<String> l1 =  strToList(str);
        List<String> l2 =  zToH(l1);
        int num = getNum(l2);
        System.out.println(num);
    }
    // 计算后缀表达式
    private static int getNum(List<String> ls) {
        Stack<String> s1 = new Stack<>();
        for (String s : ls) {
            if (s.matches("\\d+")) {
                // 如果为数字压入到s1中
                s1.push(s);
            } else {
                // 如果不为数字,从栈中弹出两个数字,进行计算,再压入栈中
                int num1 = Integer.parseInt(s1.pop());
                int num2 = Integer.parseInt(s1.pop());
                int num = cal(num1, num2, s);
                s1.push(String.valueOf(num));
            }
        }
        return Integer.parseInt(s1.pop());
    }
    //计算结果
    private static int cal(int num1, int num2, String oper) {
        int val = 0;
        switch(oper) {
            case "+":
                val = num2 + num1;
                break;
            case "-":
                val = num2 - num1;
                break;
            case "*":
                val = num2 * num1;
                break;
            case "/":
                val = num2 / num1;
                break;
        }
        return val;
    }
    // 将中缀表达式转化为后缀表达式
    private static List<String> zToH(List<String> ls) {
         Stack<String> s1 = new Stack<>(); // 存储字符
         List<String> s2 = new ArrayList<>(); //存储数字
        int i=0;
        for (String s : ls) {
            if (s.matches("\\d+")) {
                // 如果为数字,直接添加到s2中
                s2.add(s);
            } else if (s1.size() ==0 || "(".equals(s) || "(".equals(s1.peek())) {
                // 处理负数, 负数有两种情况
                // 1. 负数存在与第一位如: -3+1
                //2.负数存在与左括号后面: (-3+1)
                // 处理方法为在前面补0如: 0-3+1
                if("-".equals(s) && (i == 0 || "(".equals(ls.get(i-1)))) {
                    s2.add("0");
                }
                s1.push(s);
            } else if (")".equals(s)) {
                // 如果遇到右括号')',依次弹出s1直到遇到左括号'('
                while(!"(".equals(s1.peek())) {
                    s2.add(s1.pop());
                }
                s1.pop(); //弹出左括号
            } else {
                // 如果栈顶也为 ‘+,-,*,/’,需要比较优先级。
                //若s的优先级小于等于栈顶元素,s1弹出插入到s2中
                // 否则,将s压入到s1中
                while(s1.size() != 0 && isLeve(s) <= isLeve(s1.peek())) {
                    s2.add(s1.pop());
                }
                s1.push(s);
                
            }
            i++;
        }
        // 将s1中剩余的符号插入到s2中
        while(s1.size() != 0) {
            s2.add(s1.pop());
        }
        return s2;
    }
    // 判断优先级
    private static int isLeve(String str) {
        if ("+,-".contains(str)) {
            return 1;
        } else if("*,/".contains(str)) {
            return 2;
        } else {
            return 0;
        }
    }
    // 将中缀表达式转为字符串数组
    private static List<String>  strToList(String str) {
        char[] chars = str.toCharArray();
        List<String> ls = new ArrayList<>();
        int i = 0;
        String s = "";
        for (char ch: chars) {
            if (ch<48 || ch >57) {
                // 如果不是数字直接添加到集合中
                ls.add(ch + "");
            } else {
                // 如果为数字,则需要处理,因为数字可能为多位数
                s += ch; //数字拼接起来
                if (i == str.length()-1) {
                    // 如果当前字符串为最后一个则直接添加
                    ls.add(s);
                } else {
                    if (chars[i+1] <48 || chars[i+1] >57) {
                        ls.add(s);
                        s = ""; 
                    }
                }
            }
            i++;
        }
        return ls;
    } 
}
posted @ 2021-10-31 17:35  撑起一片阳光  阅读(488)  评论(0编辑  收藏  举报