输入描述
字符串格式的表达式,仅支持±*/,数字可能超过两位,可能带有空格,没有负数。长度小于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
java
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;
}
}
}