Skip to content

592. 分数加减运算

解题过程

INFO

题目链接 给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1

javascript
/**
 * @link https://leetcode.cn/problems/fraction-addition-and-subtraction/
 * @title 592. 分数加减运算
 * @description 给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。
这个结果应该是不可约分的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1。所以在上述例子中, 2 应该被转换为 2/1
 * @param {string} expression
 * @return {string}
 */
// 解法一
// 思路:暴力解法,难点在最简公约数上
// 第一步:正则匹配到字符串对应分数元素数组(注意:负数分数需要带上符号)
// 第二步:循环遍历数组累加求和
// 第三步:判断是否是最简分数:判断基本的是否是最简分数、循环遍历分子或分母最小值递增判断是否是公约数
var fractionAddition = function (expression) {
  const nums = expression.match(/-[\d/]+|\+?[\d/]+/g)
  let total = nums[0]

  for (let i = 1; i < nums.length; i++) {
    const perArr = total.split('/')
    const curArr = nums[i].split('/')
    if (perArr[1] === curArr[1]) {
      // 分母相同
      total = (parseInt(perArr[0]) + parseInt(curArr[0])) + '/' + perArr[1]
    } else {
      // 分母不同
      total = (parseInt(perArr[0]) * parseInt(curArr[1]) + parseInt(curArr[0]) * parseInt(perArr[1])) + '/' + (parseInt(perArr[1]) * parseInt(curArr[1]))
    }
  }

  // 判断是否是最简分数
  const [mole, deno] = total.split('/')
  let lastMole = parseInt(mole)
  let lastDeno = parseInt(deno)
  if (![1, -1, 0].includes(parseInt(mole)) && parseInt(deno) !== 1) {
    // 判断基本的是否是最简分数
    const minNum = Math.min(Math.abs(parseInt(mole)), Math.abs(parseInt(deno)))
    for (let i = 2; i <= minNum; i++) {
      if (lastMole % i === 0 && lastDeno % i === 0) {
        // 分子分母都能够整除则是公约数
        lastMole = lastMole / i
        lastDeno = lastDeno / i

        // 降一是为了当前 i 在进行一次整除判断,防止有多次相同公约数
        i = i - 1
        continue
      }

      if (Math.abs(lastMole) < i || Math.abs(lastDeno) < i) {
        // 循环除数最大值不能大于分子或分母
        break
      }
    }
  }

  // 判断分子是否为0,分子为0是因为分子求和为0,分母没有舍去,原来是整个值为0的
  return lastMole === 0 ? lastMole + '/' + 1 : lastMole + '/' + lastDeno
}

// 解法二
// 思路:来自题解的解法,还算不错的,比我做的解法一简洁一点
// 优点:正则匹配写的比我的简单,我的写法有点冗余了,执行效果更好一点
var fractionAddition = function (expression) {
  const arr = expression.match(/-?\d+\/\d+/g)
  let lastMole = 0, lastDeno = 1

  // 求分子、分母最大公倍数(注意:分子分母的计算逻辑)
  const gcb = (mole, deno) => {
    if (deno === 0) {
      // 如果分母为零,则返回分子
      return mole
    } else if (mole % deno === 0) {
      // 如果分母除以分子整除,则返回分母
      return deno
    } else {
      // 否则,调换分子分母位置,且分母改为分母除以分子的余数
      return gcb (deno, mole % deno)
    }
  }

  for (let str of arr) {
    let symbol = 1 // 符号位,用于分子计算的正负值判断

    if (str[0] === '-') {
      str = str.slice(1)
      symbol = -1
    }

    let numArr = str.split('/')
    let mole = parseInt(numArr[0]), deno = parseInt(numArr[1])
    lastMole = lastMole * deno + lastDeno * mole * symbol
    lastDeno = lastDeno * deno

    const lastNum = gcb (lastDeno, Math.abs(lastMole))
    lastMole /= lastNum
    lastDeno /= lastNum
  }

  return lastMole + '/' + lastDeno
}

// const result = fractionAddition ('-1/2+1/2') // 0/1
// const result = fractionAddition ('1/3-1/2') // -1/6
const result = fractionAddition ('-1/2+1/2+1/3') // 1/3
// const result = fractionAddition ('5/3+1/3') // 2/1
// const result = fractionAddition ('7/3+5/2-3/10') // 68/15
console.log(result)

解题感受

当初做的时候,半个小时没做出来,因为时间关系太晚了就暂时放弃做了。其实还是一点难度的,难点在于最简公约数的计算上。自己用了穷举法做的,基本考虑所有的问题写出来的,就是有点冗余,代码量有点大,执行效果还可以。解法二比较好,解题思路清晰一点,特别是计算最大公倍数的时候很简洁,值得学习

优质题解