【C/C++】592. 分数加减运算

284 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情


题目链接:592. 分数加减运算

题目描述

给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 

这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1

提示:

  • 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。 
  • 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。
  • 输入只包含合法的 最简分数,每个分数的 分子分母 的范围是  [1,10]。 如果分母是 1,意味着这个分数实际上是一个整数。
  • 输入的分数个数范围是 [1,10]
  • 最终结果 的分子与分母保证是 32 位整数范围内的有效整数。

示例 1:

输入: expression = "-1/2+1/2"
输出: "0/1"

示例 2:

输入: expression = "-1/2+1/2+1/3"
输出: "1/3"

示例 3:

输入: expression = "1/3-1/2"
输出: "-1/6"

整理题意

题目给定一个表示分数加减运算的字符串 expression,我们需要返回一个字符串形式的计算结果。

需要注意的是返回的计算结果需要是约分后的结果,如果约分后是个整数也要返回分数的形式,分母写成 1 即可。

解题思路分析

由于该题给定的字符串一定是分数加减的表达式,并且只有 +- 运算,所以整个表达式一定是 a/b + ... + c/d 的形式,所以我们可以遍历字符串,每次取出当前分数的分子和分母,然后与之前的答案进行运算,最后将分子分母约分后返回答案即可。

对于两个分数的加法运算为:x1y1+x2y2=x1×y2+x2×y1y1×y2\frac{x_{1}}{y_{1}} + \frac{x_{2}}{y_{2}} = \frac{x_{1} \times y_{2} + x_{2} \times y_{1}}{y_{1} \times y_{2}}

具体实现

  1. 初始化分子为 0,分母为 1,表示最开始的值为 0
  2. 遍历字符串以除号 '/' 作为分界线,每次取出当前字符串的分子和分母;
  3. 与之前的计算结果进行加法运算。
  4. 利用最大公约数对最终的答案进行约分化简,这里需要注意分子可能为负数,所以在求最大公约数的时候对分子进行取绝对值处理。

需要注意最后答案如果分子为 0 的话是无法进行约分的,这里要特判这种情况:当最终结果分子为 0,那么就直接输出 "0/1"

需要注意的是根据题目数据范围的提示,只说明了 最终结果 的分子与分母在 int 整型范围内,为了防止中间运算溢出我们还是使用 long long int 整型进行存储。

由于题目给定的表达式时字符串的形式,我们这里可以使用 stoll() 函数将字符串转换为 long long int 整型(stoi()->intstol()->long int),同时在输出答案可以使用 to_string() 函数进行转换,可以避免字符串与整型之间的转换代码的冗长。

复杂度分析

  • 时间复杂度:O(n+logC)O(n + \log C),其中 n 是字符串 expression 的长度,C 为化简前结果分子分母的最大值。求最大公约数需要 O(logC)O(\log C)
  • 空间复杂度:O(1)O(1)

代码实现

class Solution {
private:
    // 最大公约数 也可以直接调用#include<algorithm>库函数 __gcd(a, b);
    int GCD(int a, int b){
        return b == 0 ? a : GCD(b, a % b);
    }
    /* 联想最小公倍数(本题用不到)
    int LCM(int a, int b){
        return a * b / GCD(a, b);
    }
    */
public:
    string fractionAddition(string expression) {
        // 题目数据范围只保证最终结果没有溢出,防止中途溢出开 long long
        long long int numerator = 0, denominator = 1; //分子,分母
        int n = expression.length(), i = 0;
        while(i < n){
            // 读取分子
            string temp = "";
            while(i < n && expression[i] != '/') temp += expression[i++];
            long long int numerator1 = stoll(temp);
            i++;
            // 读取分母
            temp = "";
            while(i < n && isdigit(expression[i])) temp += expression[i++];
            long long int denominator1 = stoll(temp);
            // 通分
            numerator = denominator * numerator1 + denominator1 * numerator;
            denominator = denominator * denominator1;
        }
        if(numerator == 0) return "0/1";
        // 最大公约数,需要注意分子的负号
        int g = GCD(abs(numerator), denominator);
        return to_string(numerator / g) + '/' + to_string(denominator / g);
    }
};

总结

  • 该题核心为 数学字符串处理,在知道分数的运算法则后,剩下的就是字符串的处理和代码的实现了,也就是 暴力模拟 即可。
  • 使用手写的辗转相除法的 GCD(a, b) 函数比调用库函数 __gcd(a, b) 在运行速度上要快一点,所以更推荐手写辗转相除法的 GCD(a, b)
  • 测试结果:

592.分数加减运算.png

结束语

成功往往在多次失败之后才姗姗到来。每一次跌倒后重新站起来,都能使你的本领更强大,信心更充足。不要轻言放弃,持之以恒去做事,当挫折在脚下堆积成梯,你也就获得了进步的机会。新的一天,加油!