携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第1天,点击查看活动详情
题目描述
给定一个表示分数加减运算的字符串 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 位整数范围内的有效整数。
题目元素
给定一个字符串,字符串内容包含[1,10]的数字,+-/符号,其中+-作为运算符,/作为除法运算符。已知字符串中的数字一定能组合成分数格式。
解题思路
首先确定,运算符只包含+-,并且没有括号,所以不存在优先级的问题。运算都是从前往后。 其次,计算结果要保留分数形式,所以本题的难点在于分数运算。 分数运算需要对分数进行通分,所以这里包含两个知识点。
- 最小公倍数 两个整数公有的倍数称为公倍数,其中最小的公倍数就是最小公倍数。
- 最大公约数 能够同时被两个整数整除的最大的整数称为最大公约数。 最小公倍数和最大公约数的关系: 最小公倍数 = (两个整数的乘积)/最大公约数
代码实现
/**
* 根据表达式计算结果
*
* @param expression
* @return
*/
public static String fractionAddition(String expression) {
// 先确定存储结构
// 因为不存在优先级,是从前向后计算的
//重点是java中的分数运算
// 计算分母的最小公倍数,即求出分母的最大公约数
// 参与运算的分数,分母不能为0,需要对无效的分数进行处理;分子为0,计算结果直接为0;运算结果化简,如果结果为正,省略+号,如果结果为-,则输出-号。
char[] chars = expression.toCharArray();
// 分别用两个数组组装分数,第一位是符号,+为1,-为0,第二位是分子,第三位是分母
int[] first = new int[3];
int[] second = new int[3];
first[0] = 1;
// 是否是第一个分数
boolean firstFlag = true;
// 是否是分母
boolean denFlag = false;
for (int i =0;i < chars.length; i ++){
char aChar = chars[i];
// 判断是否是运算符
Boolean evalCharFlag = checkEvalChar(aChar);
if (evalCharFlag) {
// 运算符
if (firstFlag) {
first[0] = aChar == '+' ? 1 : 0;
}else {
second[0] = aChar == '+' ? 1 : 0;
}
denFlag = false;
}else if(aChar == '/') {
// /符号 下面是分母
denFlag = true;
}else {
int numericValue = Character.getNumericValue(aChar);
// 数字
if (firstFlag) {
if (denFlag) {
first[2] = first[2] * 10 + numericValue;
if ( i == chars.length -1 || checkEvalChar(chars[i+1])) {
// 第二个数字也封装完成之后就进行计算了
firstFlag = false;
}
}else {
first[1] = first[1] * 10 + numericValue;
}
}else {
if (denFlag) {
second[2] = second[2] * 10 + numericValue;
}else {
second[1] = second[1] * 10 + numericValue;
}
if ( i == chars.length -1 || checkEvalChar(chars[i+1])) {
// 第二个数字也封装完成之后就进行计算了
calculate(first,second);
}
}
}
}
// 将第一个数组中的元素进行约分
int maxDivisor = findMaxDivisor(first[1], first[2]);
return (first[0] == 0 ? "-" : "")+ first[1] / maxDivisor +"/"+ (first[2] / maxDivisor == 0 ? "1" : first[2] / maxDivisor);
}
/**
* 计算两个分数的最终值,并将值放在第一个数组,将第二个数组值置空
*
* @param first 第一个分数
* @param second 第二个分数
*/
private static void calculate(int[] first, int[] second) {
// 分子分母为0,直接返回第一个数组
if ( second[1] == 0 || second[2] == 0) {
return;
}
// 第一个分数分子分母为0,直接将第二个分数复制到第一个分数里面
if ( first[1] == 0 || first[2] == 0) {
first[0] = second[0];
first[1] = second[1];
first[2] = second[2];
return;
}
// 最大公约数
int maxDivisor = findMaxDivisor(first[2], second[2]);
// 根据最大公约数计算最小公倍数
int minMultiple = first[2] * second[2] / maxDivisor;
// 根据最大公约数和分母对分子进行通分
int newFirstMol = first[1] * (second[2] / maxDivisor);
int newSecondMol = second[1] * (first[2] / maxDivisor);
//计算后的分子
int newMol;
// 根据运算符号进行加减
if (first[0] == 0) {
// -
if (second[0] == 0){
// -
newMol = -newFirstMol-newSecondMol;
}else {
newMol = -newFirstMol+newSecondMol;
}
}else {
if (second[0] == 0){
// -
newMol = newFirstMol-newSecondMol;
}else {
newMol = newFirstMol+newSecondMol;
}
}
if (newMol == 0){
clearArr(first);
clearArr(second);
return;
}
// 将新生成的分子分母进行约分
// 获取分子分母的最大公约数
int newMaxDivisor = findMaxDivisor(newMol, minMultiple);
first[0] = newMol > 0 ? 1 : 0;
first[1] = Math.abs(newMol) / newMaxDivisor;
first[2] = minMultiple / newMaxDivisor;
clearArr(second);
}
/**
* 将指定数组置空
*
* @param arr 需要置空的数组
*/
private static void clearArr(int[] arr) {
arr[0] = 1;
arr[1] = 0;
arr[2] = 0;
}
/**
* 判断字符是否是运算符
*
* @param aChar 字符
* @return true=是运算符 false=不是运算符
*/
private static Boolean checkEvalChar(char aChar) {
return aChar == '-' || aChar == '+' ;
}
/**
* 计算两个数的最大公约数
*
* @param num1 数字1
* @param num2 数字2
* @return
*/
private static int findMaxDivisor(int num1,int num2){
// 最小公倍数 两个整数公有的倍数叫做公倍数,其中最小的公倍数为最小公倍数
// 最大公约数 能够同时被两个数整除的最大的数
// 最小公倍数 = 两个整数的积 ➗ 最大公约数
if ( num2 == 0 ) {
return 1;
}
int max = Math.abs(num2);
int min = Math.abs(num1);
if (num1 > num2) {
max = Math.abs(num1);
min = Math.abs(num2);
}
int temp = max % min;
if (temp == 0) {
return min;
}else {
return findMaxDivisor(min,temp);
}
}
public static void main(String[] args) {
System.out.println(fractionAddition("3/10+1/5-10/7-5/6-2/5"));
}