592. 分数加减运算
作者:互联网
题目描述:
给定一个表示分数加减运算的字符串 expression ,你需要返回一个字符串形式的计算结果。 这个结果应该是 不可约分 的分数,即最简分数。 如果最终结果是一个整数,例如 2,你需要将它转换成分数形式,其分母为 1,所以 2 应该被转换为 2/1。
提示:
- 输入和输出字符串只包含 '0' 到 '9' 的数字,以及 '/', '+' 和 '-'。
- 输入和输出分数格式均为 ±分子/分母。如果输入的第一个分数或者输出的分数是正数,则 '+' 会被省略掉。
- 输入只包含合法的最简分数,每个分数的分子与分母的范围是 [1,10]。 如果分母是1,意味着这个分数实际上是一个整数。
- 输入的分数个数范围是 [1,10]。
- 最终结果的分子与分母保证是 32 位整数范围内的有效整数。
示例: 输入:expression = "-5/2+10/3+7/9" 输出:"29/18"
解题思路:
我没有采用将所有分母一起通分的方法,因为考虑到如果分母很大的情况下,全部一起通分可能会溢出(实际上,对于该题的条件来说这样的考虑是多余的- -!)。我采取的做法是,每次将相邻两个分母通分,然后计算相应的分子,最后对分子分母进行约分,计算结果作为“前一个分数”。
这里,我定义:
- pre_mu:前一个分数的分母
- pre_zi:前一个分数的分子
- pre_sym:前一个分数的正负号(-1或1)
- now_mu:当前分数的分母
- now_zi:当前分数的分子
- now_sym:当前分数的正负号
注意,我的代码逻辑中,每次处理“前一个分数与当前分数相加减的计算结果”的时机是遍历到下一个分数的正负号时,计算完毕后将计算结果作为“前一个分数”。如示例中,循环走到“+10/3”的‘+’时,代码才开始处理前一个分数“0/1”(初始值)与当前分数“-5/2”的计算结果,然后将结果“-5/2”作为“前一个分数”;然后继续提取“+10/3”各项值。当下一次走到“+7/9”的“+”时,才处理“-5/2”与“+10/3”的计算结果。
num变量先以字符串的形式存储分子(有可能是一个数字,也可能是两个),当遍历到“/”符号时,才将字符串num的数字转换为数值,存储在now_zi中,并将num置空;然后num接着存储“/”后的数字,当遍历到下一个“-”或“+”号时,同样将字符串num转换为数值,作为分母存储在now_mu中,并进行相应的计算,计算出新的pre_mu、pre_zi与pre_sym。
步骤如下:
依次遍历expression中的字符,
- 当字符为'-'或‘+’时,先将字符串num转化为数字作为分母,然后计算前面两个分数的运算结果,将结果作为新的前一个分数;并且,保存当前分数的正负号;
- 当字符为数字时,将其添加在字符串num后面(应对分子或分母为10的情况);
- 若不满足上面两点,也即字符为'/'时,将字符串num转化为数字作为分子,同时初始化num = 0;
- 遍历完expression后,我们还未计算最后一个分数与前一个分数运算的值,需要多写一次操作。
代码实现:
/** * @param {string} expression * @return {string} */ var fractionAddition = function(expression) { //初始化,假设"前一个分数"为0/1,这不影响最后的计算结果 let pre_zi = 0; let pre_mu = 1; let pre_sym = 1; let num = ''; let now_zi = 0; let now_mu = 0; let now_sym = 1; for(let i=0;i<expression.length;i++){ if(expression[i]==='-'||expression[i]==='+'){ //若num为'',表示是第一个数,此时只有"前一个分数0/1",凑不齐两个分数,不作处理; //若num不为空,表示前面已经已经"前一个分数"与"当前分数",进行计算 if(num){ now_mu = parseInt(num);//提取当前分数的分母 num = ''; let new_mu = computedMu(pre_mu,now_mu);//计算两分母通分后的结果 let new_zi = pre_sym*(new_mu/pre_mu)*pre_zi + now_sym*(new_mu/now_mu)*now_zi;//计算分子 //分子分母约分前,先对分子进行处理 if(new_zi<0){ pre_sym = -1; new_zi = - new_zi; }else{ pre_sym = 1; } //使用辗转相除法计算出最大公约数 let res = gcd(new_zi,new_mu); //若分子或分母存在零值(分母不可能为0),将"前一个分数置为0/1" if(res===0){ pre_zi = 0; pre_mu = 1; }else{ //约分后的结果 pre_zi = new_zi/res; pre_mu = new_mu/res; } } //别忘了保存遍历到的分数的正负号 now_sym = expression[i]==='-'?-1:1; continue; }else if(parseInt(expression[i]).toString()!=='NaN'){//若遍历到的字符为数字,先添加到字符串num末尾 num += expression[i]; continue; }else{ //当遍历到"/"符号时,提取出当前分数的分子 now_zi = parseInt(num); num =''; } } //遍历结束后,别忘了我们每次处理"前一个分数与当前分数相加减的计算结果"的时机是遍历到下一个分数的正负号时, // 此时我们还未处理最后一个分数与前一个分数 now_mu = parseInt(num); let new_mu = computedMu(pre_mu,now_mu); let new_zi = pre_sym*(new_mu/pre_mu)*pre_zi + now_sym*(new_mu/now_mu)*now_zi; if(new_zi<0){ pre_sym = -1; new_zi = - new_zi; }else { pre_sym = 1; } let res = gcd(new_zi,new_mu); //若最大公约数为0,表示结果的分子存在零值,直接返回"0/1" if(res===0){ return '0/1'; }else{ pre_zi = new_zi/res; pre_mu = new_mu/res; } let sym = pre_sym===-1?'-':''; return sym + pre_zi +'/'+pre_mu; }; //计算两分母通分后的新分母,也即最大公倍数 function computedMu(n1,n2){ let res = gcd(n1,n2); return (n1/res)*(n2/res)*res; } //求最大公约数 function gcd(n1,n2){ if(n1===0||n2===0){ return 0; } while(n2!==0){ let res = n1 %n2; n1 = n2 ; n2 = res; } return n1; }
标签:分数,pre,592,加减,num,let,now,分母 来源: https://www.cnblogs.com/evil-shark/p/16525784.html