符号运算 - 华为OD统一考试

news/2024/10/17 20:12:43/

OD统一考试(C卷)

分值: 200分

题解: Java / Python / C++

alt

题目描述

给定一个表达式,求其分数计算结果

表达式的限制如下:

  1. 所有的输入数字皆为正整数(包括0)
  2. 仅支持四则运算(±*/)和括号
  3. 结果为整数或分数, 分数必须化为最简格式(比如6, 3/4, 7/8, 90/7)
  4. 除数可能为0,如果遇到这种情况,直接输出"ERROR"
  5. 输入和最终计算结果中的数字都不会超出整型范围

用例的输入一定合法, 不会出现括号不匹配的情况

输入描述

字符串格式的表达式,仅支持±*/,数字可能超过两位,可能带有空格,没有负数

长度小于200个字符

输出描述

表达式结果,以最简格式表达
如果结果为整数,那么直接输出整数
如果结果为分数,那么分子分母不可再约分,可以为假分数,不可表达为带分数
结果可能是负数, 负号放在最前面

示例1

输入:
1 + 5 * 7 / 8输出:
43/8

示例2

输入:
1 / (0-5)输出:
-1/5

示例3

输入:
1 * (3*4/(8-(7+0)))输出:
12

题解

这是一个计算表达式的题目,涉及到四则运算和括号的处理。主要思路是通过两个,一个存放数字,一个存放运算符,按照运算符的优先级进行计算。以下是对代码的一些解释:

  1. Fraction 类用于表示分数,包含了分子 fz 和分母 fm,以及一些基本的运算方法(加、减、乘、除)和简化分数的方法。
  2. Solution 类实现了计算表达式的方法 calculate,以及辅助方法 calc 用于处理运算。
  3. calculate 方法中,通过遍历输入的表达式字符数组,根据字符的类型进行相应的处理。如果是数字,则将连续的数字字符组合成一个整数,并添加到数字栈中。如果是运算符,则根据运算符的优先级进行相应的计算。括号的处理通过检测左括号入栈,右括号时计算直到遇到左括号。
  4. Fraction 类中的 simplify 方法用于简化分数,使用最大公约数来除去分子分母的公约数,得到最简形式的分数。
  5. calc 方法中,根据当前运算符的优先级,判断是否需要进行运算,如果需要则从数字栈中取出两个数字和运算符进行计算,并将结果放回数字栈。
  6. 最终,得到的结果为数字栈中最后剩下的一个分数,输出其最简形式。

关于时间复杂度和空间复杂度:

  • 时间复杂度:遍历输入字符串一次,每个字符进行常数时间的操作,因此时间复杂度为 O(n)。
  • 空间复杂度:使用两个栈来存放数字和运算符,最坏情况下可能同时存放整个表达式的内容,因此空间复杂度为 O(n)。

Java

import java.util.*;
/*** @author code5bug*/
public class Main {public static void main(String[] args) {Scanner scanner = new Scanner(System.in);Solution solution = new Solution();try {String s = scanner.nextLine().replaceAll(" ", "");System.out.println(solution.calculate(s.toCharArray()));} catch (Exception e) {System.out.println("ERROR");}}
}class Solution {Map<Character, Integer> map;public Solution() {// 符号优先级this.map = new HashMap<>();this.map.put('-', 1);this.map.put('+', 1);this.map.put('*', 2);this.map.put('/', 2);}public Fraction calculate(char[] cs) {int n = cs.length;Deque<Fraction> nums = new ArrayDeque<>();         // 存放所有的数字Deque<Character> ops = new ArrayDeque<>();      	// 存放所有「非数字以外」的操作nums.addLast(new Fraction(0, 1));      // 为了防止第一个数为负数,先往 nums 加个 0for (int i = 0; i < n; i++) {char c = cs[i];if (c == '(') {ops.addLast(c);} else if (c == ')') {// 计算到最近一个左括号为止while (!ops.isEmpty()) {if (ops.peekLast() != '(') {calc(nums, ops);} else {ops.pollLast();break;}}} else {if (isNumber(c)) {int u = 0, j = i;// 将从 i 位置开始后面的连续数字整体取出,加入 numswhile (j < n && isNumber(cs[j])) u = u * 10 + (cs[j++] - '0');nums.addLast(new Fraction(u, 1));i = j - 1;} else {if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {nums.addLast(new Fraction(0, 1));}while (!ops.isEmpty() && ops.peekLast() != '(') {char prev = ops.peekLast();if (map.get(prev) >= 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) {if (nums.size() < 2 || ops.isEmpty()) return;Fraction b = nums.pollLast(), a = nums.pollLast();char op = ops.pollLast();Fraction ans = null;if (op == '+') ans = a.add(b);else if (op == '-') ans = a.subtract(b);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);}
}/*** 分数类*/
class Fraction {long fz;  // 分子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 nfz = this.fz * other.fm - other.fz * this.fm;long nfm = this.fm * other.fm;return new Fraction(nfz, nfm).simplify();}// 乘法Fraction multiply(Fraction other) {long nfz = this.fz * other.fz;long nfm = this.fm * other.fm;return new Fraction(nfz, nfm).simplify();}// 触发Fraction divide(Fraction other) {if (other.fz == 0) throw new ArithmeticException("ERROR");long nfz = this.fz * other.fm;long nfm = this.fm * other.fz;return new Fraction(nfz, nfm).simplify();}// 简化分母Fraction simplify() {long gcd = gcd(this.fz, this.fm);return new Fraction(this.fz / gcd, this.fm / gcd);}// 求两个数的最小公倍数public long gcd(long a, long b) {return a % b == 0 ? b : gcd(b, a % b);}public String toString() {if (fm == 1) {return Long.toString(fz);} else {String rs = Math.abs(fz) + "/" + Math.abs(fm);if (fz * fm < 0) rs = "-" + rs;return rs;}}
}

Python

import mathclass Fraction:def __init__(self, fz, fm):self.fz = fz  # 分子self.fm = fm  # 分母# 加法def add(self, other):nfz = self.fz * other.fm + other.fz * self.fmnfm = self.fm * other.fmreturn Fraction(nfz, nfm).simplify()# 减法def subtract(self, other):nfz = self.fz * other.fm - other.fz * self.fmnfm = self.fm * other.fmreturn Fraction(nfz, nfm).simplify()# 乘法def multiply(self, other):nfz = self.fz * other.fznfm = self.fm * other.fmreturn Fraction(nfz, nfm).simplify()# 触发def divide(self, other):if other.fz == 0:raise RuntimeError("ERROR")nfz = self.fz * other.fmnfm = self.fm * other.fzreturn Fraction(nfz, nfm).simplify()# 简化分母def simplify(self):gcd_val = self.gcd(abs(self.fz), abs(self.fm))return Fraction(self.fz // gcd_val, self.fm // gcd_val)# 求两个数的最大公约数def gcd(self, a, b):return a if b == 0 else self.gcd(b, a % b)def to_string(self):if self.fm == 1:return str(self.fz)else:rs = str(abs(self.fz)) + "/" + str(abs(self.fm))if self.fz * self.fm < 0:rs = "-" + rsreturn rsclass Solution:def __init__(self):# 符号优先级self.map = {'-': 1, '+': 1, '*': 2, '/': 2}def calculate(self, cs):n = len(cs)nums = []  # 存放所有的数字ops = []   # 存放所有「非数字以外」的操作nums.append(Fraction(0, 1))  # 为了防止第一个数为负数,先往 nums 加个 0i = 0while i < n:c = cs[i]if c == '(':ops.append(c)elif c == ')':# 计算到最近一个左括号为止while ops:if ops[-1] != '(':self.calc(nums, ops)else:ops.pop()breakelse:if c.isdigit():u, j = 0, i# 将从 i 位置开始后面的连续数字整体取出,加入 numswhile j < n and cs[j].isdigit():u = u * 10 + int(cs[j])j += 1nums.append(Fraction(u, 1))i = j - 1else:if i > 0 and (cs[i - 1] == '(' or cs[i - 1] == '+' or cs[i - 1] == '-'):nums.append(Fraction(0, 1))while ops and ops[-1] != '(':prev = ops[-1]if self.map[prev] >= self.map[c]:self.calc(nums, ops)else:breakops.append(c)i += 1# 将剩余的计算完while ops:self.calc(nums, ops)return nums[-1]def calc(self, nums, ops):if len(nums) < 2 or not ops:returnb = nums.pop()      # 弹出数字a = nums.pop()      # 弹出数字op = ops.pop()      # 弹出操作符if op == '+':nums.append(a.add(b))elif op == '-':nums.append(a.subtract(b))elif op == '*':nums.append(a.multiply(b))elif op == '/':nums.append(a.divide(b))if __name__ == "__main__":solution = Solution()expression = input().replace(" ", "")  # 移除空格try:result = solution.calculate(expression)print(result.to_string())except Exception as e:print("ERROR")

C++

#include <bits/stdc++.h>using namespace std;class Fraction {
public:long fz;  // 分子long fm; // 分母Fraction(long fz, long fm) : fz(fz), fm(fm) {}// 加法Fraction add(const Fraction& other) const {long nfz = this->fz * other.fm + other.fz * this->fm;long nfm = this->fm * other.fm;return Fraction(nfz, nfm).simplify();}// 减法Fraction subtract(const Fraction& other) const {long nfz = this->fz * other.fm - other.fz * this->fm;long nfm = this->fm * other.fm;return Fraction(nfz, nfm).simplify();}// 乘法Fraction multiply(const Fraction& other) const {long nfz = this->fz * other.fz;long nfm = this->fm * other.fm;return Fraction(nfz, nfm).simplify();}// 触发Fraction divide(const Fraction& other) const {if (other.fz == 0) throw runtime_error("ERROR");long nfz = this->fz * other.fm;long nfm = this->fm * other.fz;return Fraction(nfz, nfm).simplify();}// 简化分母Fraction simplify() const {long gcd_val = gcd(abs(fz), abs(fm));return Fraction(fz / gcd_val, fm / gcd_val);}// 求两个数的最大公约数long gcd(long a, long b) const {return (b == 0) ? a : gcd(b, a % b);}string toString() const {if (fm == 1) {return to_string(fz);} else {string rs = to_string(abs(fz)) + "/" + to_string(abs(fm));if (fz * fm < 0) rs = "-" + rs;return rs;}}
};class Solution {
public:unordered_map<char, int> map;Solution() {// 符号优先级map['-'] = 1;map['+'] = 1;map['*'] = 2;map['/'] = 2;}Fraction calculate(const char* cs) {int n = strlen(cs);deque<Fraction> nums;    // 存放所有的数字deque<char> ops;         // 存放所有「非数字以外」的操作nums.push_back(Fraction(0, 1));  // 为了防止第一个数为负数,先往 nums 加个 0for (int i = 0; i < n; i++) {char c = cs[i];if (c == '(') {ops.push_back(c);} else if (c == ')') {// 计算到最近一个左括号为止while (!ops.empty()) {if (ops.back() != '(') {calc(nums, ops);} else {ops.pop_back();break;}}} else {if (isdigit(c)) {int u = 0, j = i;// 将从 i 位置开始后面的连续数字整体取出,加入 numswhile (j < n && isdigit(cs[j])) u = u * 10 + (cs[j++] - '0');nums.push_back(Fraction(u, 1));i = j - 1;} else {if (i > 0 && (cs[i - 1] == '(' || cs[i - 1] == '+' || cs[i - 1] == '-')) {nums.push_back(Fraction(0, 1));}while (!ops.empty() && ops.back() != '(') {char prev = ops.back();if (map[prev] >= map[c]) {calc(nums, ops);} else {break;}}ops.push_back(c);}}}// 将剩余的计算完while (!ops.empty()) calc(nums, ops);return nums.back();}void calc(deque<Fraction>& nums, deque<char>& ops) {if (nums.size() < 2 || ops.empty()) return;Fraction b = nums.back();        nums.pop_back();Fraction a = nums.back();        nums.pop_back();char op = ops.back();        ops.pop_back();if (op == '+') nums.push_back(a.add(b));else if (op == '-') nums.push_back(a.subtract(b));else if (op == '*') nums.push_back(a.multiply(b));else if (op == '/') nums.push_back(a.divide(b));}};int main() {Solution solution;char buffer[210];cin.getline(buffer, sizeof(buffer));string s(buffer);s.erase(remove_if(s.begin(), s.end(), ::isspace), s.end());  // 移除空格try {Fraction result = solution.calculate(s.c_str());cout << result.toString() << endl;} catch (const exception& e) {cout << "ERROR" << endl;}return 0;
}

相关练习题

题号题目难易
LeetCode 面试题 16.26面试题 16.26. 计算器中等
LeetCode 227227. 基本计算器 II中等
LeetCode 224224. 基本计算器困难

‍❤️‍华为OD机试面试交流群每日真题分享): 加V时备注“华为od加群”

🙏整理题解不易, 如果有帮助到您,请给点个赞 ‍❤️‍ 和收藏 ⭐,让更多的人看到。🙏🙏🙏


http://www.ppmy.cn/news/1341738.html

相关文章

树莓派zero/zero w的区别

直观区别 1、zero没有WiFi和蓝牙模块&#xff0c;当然也没有网线接口&#xff0c;适合不需要网络的场景需求。 2、zero w带有WiFi和蓝牙模块&#xff0c;没有网线接口。适合需要网络的场景需求。 选购建议 我一般都是看有没有网络接口或者WiFi支持&#xff08;一定要选择焊接…

Leetcode206:反转链表

一、题目 给你单链表的头节点 head &#xff0c;请你反转链表&#xff0c;并返回反转后的链表 示例&#xff1a; 输入&#xff1a;head [1,2,3,4,5] 输出&#xff1a;[5,4,3,2,1]输入&#xff1a;head [1,2] 输出&#xff1a;[2,1]输入&#xff1a;head [] 输出&#xff1…

JavaScript阻止浏览器默认行为

&#x1f9d1;‍&#x1f393; 个人主页&#xff1a;《爱蹦跶的大A阿》 &#x1f525;当前正在更新专栏&#xff1a;《VUE》 、《JavaScript保姆级教程》、《krpano》、《krpano中文文档》 ​ ​ ✨ 前言 浏览器对一些事件具有默认行为,例如点击链接时跳转页面,提交表单时发…

升级 FATFS 笔记

最近有朋友希望 AWTK demo 中的 FATFS 能升级到最新版本&#xff0c;在升级的过程中遇到一些小问题&#xff0c;这里做个记录。 1. 升级 FATFS 从官网下载最新代码。更新下面的文件到AWTK项目中&#xff1a; ff.cff.hffsystem.cffunicode.c 下面的文件不需要更新&#xff1…

Linux软件编程以及IO输入输出——linux——day1

Linux软件编程以及其IO输入输出 Linux软件编程 linux是操作系统的内核 主要有以下几个功能&#xff1a; ①管理CPU ②管理内存 ③管理硬件设备 ④管理文件系统 ⑤任务调度 shell指令 shell命令主要保护Linux内核(用户和Linux内核不直接操作,通过操作Shell,Shell和内核交互…

vue3 markdown编辑器推荐(maven-editor vditor tiptap )

最近项目需要用到markdown编辑器&#xff0c;使用了三种 maven-editor (http://www.mavoneditor.com/?spma2c6h.12873639.article-detail.9.aaad62affAKmTV)vditor (https://b3log.org/vditor/demo/index.html?utm_sourceld246.com)tiptap (https://github.com/ueberdosis/t…

网络安全之SSL证书加密

简介 SSL证书是一种数字证书&#xff0c;遵守SSL协议&#xff0c;由受信任的数字证书颁发机构&#xff08;CA&#xff09;验证服务器身份后颁发。它具有服务器身份验证和数据传输加密的功能&#xff0c;能够确保数据在传输过程中的安全性和完整性。 具体来说&#xff0c;SSL证…

QT 应用中集成 Sentry

QT 应用中集成 Sentry QT应用中集成 SentrySentry SDK for C/C注册 Sentry 账号QT 应用中集成 Sentry触发 Crash 上报 QT应用中集成 Sentry Sentry 是一个开源的错误监控和日志记录平台&#xff0c;旨在帮助开发团队实时捕获、跟踪和解决软件应用程序中的错误和异常。它提供了…