符号运算(华为OD)
输入描述
字符串格式的表达式,仅支持±*/,数字可能超过两位,可能带有空格,没有负数。长度小于200个字符。
输出描述
表达式结果,以最简格式表达 如果结果为整数,那么直接输出整数 如果结果为分数,那么分子分母不可再约分,可以为假分数,不可表达为带分数 结果可能是负数, 负号放在最前面。
示例1
输入:
1 + 5 * 7 / 8
输出:
43/8
示例2
输入:
1 / (0-5)
输出:
-1/5
示例3
输入:
1 * (3*4/(8-(7+0)))
输出:
12
示例4
输入:
(1+2)*3-4/2
输出:
7
package com.study.algorithm.huaweiOrOD.huaweiOD202509082334.符号运算;import java.util.*;/*** @ProjectName: 算法* @ClassName: Main* @Description: 符号运算* @Author: Tony_Yi* @Date: 2025/10/4 11:05* @Version 1.0**/
public class Main {public static void main(String[] args) {// 1. 创建Scanner对象,用于读取控制台输入Scanner scan = new Scanner(System.in);// 2. 创建Solution实例(核心计算逻辑的载体)Solution solution = new Solution();try {// 3. 读取输入:去掉所有空格(避免"1 + 2"这类带空格的输入影响解析)String res = scan.nextLine().replaceAll(" ", "");// 4. 计算并打印结果:字符串转字符数组传给calculate,结果自动调用Fraction的toString()格式化System.out.println(solution.calculate(res.toCharArray()));} catch (Exception e) {// 5. 捕获所有异常(如除零错误、表达式格式错误),输出"Error"System.out.println("Error");}}
}class Solution {// 存储运算符优先级:key=运算符,value=优先级(数字越大优先级越高)Map<Character, Integer> map;// 构造方法:初始化运算符优先级public Solution() {this.map = new HashMap<>();this.map.put('+', 1); // 加减优先级1(低)this.map.put('-', 1);this.map.put('*', 2); // 乘除优先级2(高)this.map.put('/', 2);}public Fraction calculate(char[] cs) {int n = cs.length; // 表达式长度// 双栈初始化:Deque<Fraction> nums = new ArrayDeque<>(); // 数字栈(存分数对象)Deque<Character> ops = new ArrayDeque<>(); // 运算符栈(存运算符/括号)// 关键初始化:添加0到数字栈,处理表达式以负数开头的情况(如"-1+2" → "0-1+2")nums.addLast(new Fraction(0, 1));// 遍历表达式的每个字符for (int i = 0; i < n; i++) {char c = cs[i];// 情况1:遇到左括号'(' → 直接压入运算符栈(等待右括号触发计算)if (c == '(') {ops.addLast(c);} else if (c == ')') {// 情况2:遇到右括号')' → 计算到最近的左括号为止// 循环弹出运算符并计算,直到栈顶是'('while (!ops.isEmpty()) {if (ops.peekLast() != '(') { // 栈顶不是左括号 → 计算calc(nums, ops);} else { // 遇到左括号 → 弹出左括号(括号内计算完成),退出循环ops.pollLast();break;}}} else {// 情况3:既不是括号 → 数字或运算符// 子情况3.1:当前字符是数字 → 解析连续数字(处理多位数,如"123")if (isNumber(c)) {int u = 0; // 存储解析出的整数int j = i; // 从当前位置i开始,遍历连续数字while (j < n && isNumber(cs[j])) {// 字符转数字:'0'的ASCII码是48,cs[j]-'0'得到实际数字(如'5'-'0'=5)u = u * 10 + (cs[j++] - '0');}// 整数转分数(分子=u,分母=1),压入数字栈nums.addLast(new Fraction(u, 1));i = j - 1; // i跳至最后一个数字的位置(避免循环重复处理)} else {// 子情况3.2:当前字符是运算符(+、-、*、/)// 关键处理:避免运算符前无数字的情况(如"(+1)"→"(0+1)"、"1+-2"→"1+0-2")if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {nums.addLast(new Fraction(0, 1)); // 补0到数字栈}// 处理运算符优先级:栈顶运算符优先级≥当前运算符 → 先计算栈顶(先乘除后加减)while (!ops.isEmpty() && ops.peekLast() != '(') {char prevOp = ops.peekLast(); // 栈顶运算符// 栈顶优先级更高或相等 → 计算if (map.get(prevOp) >= map.get(c)) {calc(nums, ops);} else {break; // 栈顶优先级低,停止计算,当前运算符等待后续处理}}// 当前运算符压入运算符栈ops.addLast(c);}}}// 遍历结束后,运算符栈可能还有剩余运算符 → 逐个计算while (!ops.isEmpty()) {calc(nums, ops);}// 数字栈的最后一个元素就是最终结果return nums.peekLast();}void calc(Deque<Fraction> nums, Deque<Character> ops) {// 防御判断:至少需要2个操作数和1个运算符才能计算if (nums.size() < 2 || ops.isEmpty()) {return;}// 弹出操作数:注意顺序!后弹出的是第一个操作数(如"a+b",数字栈是[a,b],弹出b再弹出a)Fraction b = nums.pollLast(); // 第二个操作数(如"a-b"中的b)Fraction a = nums.pollLast(); // 第一个操作数(如"a-b"中的a)char op = ops.pollLast(); // 弹出运算符Fraction ans = null; // 计算结果// 根据运算符调用Fraction的对应方法if (op == '+') {ans = a.add(b); // 加法} else if (op == '-') {ans = a.subtract(b); // 减法(a - b,不是b - a)} else if (op == '*') {ans = a.multiply(b); // 乘法} else if (op == '/') {ans = a.divide(b); // 除法}// 计算结果压回数字栈nums.addLast(ans);}boolean isNumber(char c) {return Character.isDigit(c); // 调用Java内置方法判断}
}class Fraction {long fz; // 分子(用long避免整数溢出,如1e9*1e9超出int范围)long fm; // 分母// 构造方法:初始化分子和分母Fraction(long fz, long fm) {this.fz = fz;this.fm = fm;}Fraction add(Fraction other) {long nfz = this.fz * other.fm + other.fz * this.fm;long nfm = this.fm * other.fm;return new Fraction(nfz, nfm).simplify();}Fraction subtract(Fraction other) {long newFz = this.fz * other.fm - other.fz * this.fm; // 新分子(a*d - c*b)long newFm = this.fm * other.fm; // 新分母return new Fraction(newFz, newFm).simplify(); // 化简后返回}Fraction multiply(Fraction other) {long newFz = this.fz * other.fz; // 新分子(a*c)long newFm = this.fm * other.fm; // 新分母(b*d)return new Fraction(newFz, newFm).simplify(); // 化简后返回}Fraction divide(Fraction other) {// 除零检查:other的分子为0 → 分数值为0,除法无意义if (other.fz == 0) {throw new ArithmeticException("Error"); // 抛异常,被Main捕获}long newFz = this.fz * other.fm; // 新分子(a*d)long newFm = this.fm * other.fz; // 新分母(b*c)return new Fraction(newFz, newFm).simplify(); // 化简后返回}Fraction simplify() {long gcdVal = gcd(this.fz, this.fm); // 计算分子和分母的最大公约数// 分子分母同除以最大公约数,得到最简分数return new Fraction(this.fz / gcdVal, this.fm / gcdVal);}public long gcd(long a, long b) {return a % b == 0 ? b : gcd(b, a % b);}public String toString() {if (this.fm == 1) {// 分母为1 → 整数,直接输出分子(如3/1→"3")return Long.toString(this.fz);} else {// 分母不为1 → 拼接绝对值(避免"-3/-2"这类冗余符号)String base = Math.abs(this.fz) + "/" + Math.abs(this.fm);// 判断符号:分子分母异号(乘积<0)→ 加负号(如-3/2或3/-2→"-3/2")if (this.fz * this.fm < 0) {base = "-" + base;}return base;}}
}