分数加减运算

163 阅读4分钟

这是我参与「掘金日新计划 · 2 月更文挑战」的第 29 天,点击查看活动详情

问题描述

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

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

示例 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"

提示:

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

思路分析

首先我们先要理解一下题目意思,题目会给我们一个表示分数加减运算的字符串 expression,我们需要对给出的式子进行解析运算,这其实就是要我们模拟一遍我们小学学过的分数运算来进行解题,我们可以分为以下几步来解答题目:

1、提取字符串中的运算符和分数

我们需要先将字符串中的运算符和分数分离提取出来,这里我们需要注意:如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉,也就是说如果第一个分数不是正数的话我们需要在前面补上一个+运算符,使运算符和分数可以一一对应起来。

  • 提取字符串中的分数字符

获取字符串中所有分数值我们可以直接使用正则表达式加split函数进行分割:

const nums = expression.split(/\+|\-/);
if (nums[0] == "") nums.shift();
  • 提取字符串中的运算符

我们可以直接遍历一遍字符串,将遇到的运算符按顺序记录起来即可:

const flag = [];
if (expression[0] != "-") {
  flag.push("+");
}
let str = "";
for (let i = 0; i < expression.length; i++) {
  if (["+", "-"].includes(expression[i])) {
    flag.push(expression[i]);
  }
}
  • 遍历同时提取运算符和分数

我们也可以不使用正则表达式来提取分数,我们可以再遍历提取运算符的同时将分数也提取出来

const nums = [];
const flag = [];
if (!["+", "-"].includes(expression[0])) {
  flag.push("+");
}
let str = "";
for (let i = 0; i < expression.length; i++) {
  if (["+", "-"].includes(expression[i])) {
    flag.push(expression[i]);
    nums.push(str);
    str = "";
  } else {
    str += expression[i];
  }
}
if (str) nums.push(str);
if (nums[0] == "") nums.shift();

2、计算两个分数之和

提取出运算符和分数之后,我们便可以开始进行计算了,分数计算是我们小学学过的知识了,这里我们可以回忆一下,计算过程主要分为以下几个步骤:

  • (1)分数通分

对于两个不同分母的分数,我们应该先对两个分数进行通分,通分其实就是要找到两个分母的最小公倍数,所以我们需要先获取两个分母的最小公倍数,最小公倍数我们可以通过最小公约数来快速求得,代码如下:

//获取两数的最大公约数
const gcd = (a, b) => {
  return a % b == 0 ? b : gcd(b, a % b);
};
//获取两数的最小公倍数
const getLcm = (a, b) => {
  return (a * b) / gcd(a, b);
};
  • (2)分子相加 获取到最小公倍数之后,我们需要可以将两个分数的分母都变为最小公倍数,分子也应进行对应的变化,最后将两个分子相加作为新的分子,最小公倍数作为新的分母即可,具体代码如下:
//获取最小公倍数
const denominator = getLcm(a[1], b[1]);
//通分后的分子1
const moleculeA = (a[0] * denominator) / a[1];
//通分后的分子2
const moleculeB = (b[0] * denominator) / b[1];
let tmp = 0;
//将通分后的分子相加
tmp += moleculeA * (fA == "-" ? -1 : 1);
tmp += moleculeB * (fB == "-" ? -1 : 1);
  • (3)分数约分

获得相加后的分数之后,我们还需要对新分数进行约分,约分其实就是找出分子和分母的最大公约数,同时除于最大公约数即可:

//获取最大公约数
const div = gcd(Math.abs(tmp), denominator);
//分子分母同时除于最大公约数
return (tmp > 0 ? "+" : "") + tmp / div + "/" + denominator / div;

遍历计算整个式子的和

let res = 0;
for (let i = 0; i < nums.length; i++) {
  res = calculate(res, flag[i] + nums[i]);
}

完整 AC 代码如下:

AC 代码

/**
 * @param {string} expression
 * @return {string}
 */
var fractionAddition = function (expression) {
  const nums = [];
  const flag = [];
  if (!["+", "-"].includes(expression[0])) {
    flag.push("+");
  }
  let str = "";
  for (let i = 0; i < expression.length; i++) {
    if (["+", "-"].includes(expression[i])) {
      flag.push(expression[i]);
      nums.push(str);
      str = "";
    } else {
      str += expression[i];
    }
  }
  if (str) nums.push(str);
  if (nums[0] == "") nums.shift();
  let res = 0;
  const gcd = (a, b) => {
    return a % b == 0 ? b : gcd(b, a % b);
  };
  const getLcm = (a, b) => {
    return (a * b) / gcd(a, b);
  };
  const calculate = (a, b) => {
    if (a == 0) return b;
    if (b == 0) return a;
    const fA = a[0],
      fB = b[0];
    a = a.slice(1).split("/");
    b = b.slice(1).split("/");
    const denominator = getLcm(a[1], b[1]);
    const moleculeA = (a[0] * denominator) / a[1];
    const moleculeB = (b[0] * denominator) / b[1];
    let tmp = 0;
    tmp += moleculeA * (fA == "-" ? -1 : 1);
    tmp += moleculeB * (fB == "-" ? -1 : 1);
    const div = gcd(Math.abs(tmp), denominator);
    return (tmp > 0 ? "+" : "") + tmp / div + "/" + denominator / div;
  };
  for (let i = 0; i < nums.length; i++) {
    res = calculate(res, flag[i] + nums[i]);
  }
  return res[0] == "+" ? res.slice(1) : res;
};

说在后面

本人为算法业余爱好者,平时只是随着兴趣偶尔刷刷题,如果上面分享有错误的地方,欢迎指出,感激不尽。