给定两个整数,分别表示分数的分子 numerator
和分母 denominator
,以 字符串形式返回小数 。
如果小数部分为循环小数,则将循环的部分括在括号内。
如果存在多个答案,只需返回 任意一个 。
对于所有给定的输入, 保证 答案字符串的长度小于 104
。
示例 1:
**输入:** numerator = 1, denominator = 2
**输出:** "0.5"
示例 2:
**输入:** numerator = 2, denominator = 1
**输出:** "2"
示例 3:
**输入:** numerator = 4, denominator = 333
**输出:** "0.(012)"
提示:
-231 <= numerator, denominator <= 231 - 1
denominator != 0
方法一:长除法
题目要求根据给定的分子和分母,将分数转成整数或小数。由于给定的分子和分母的取值范围都是 $[-2^{31}, 2^{31}-1]$,为了防止计算过程中产生溢出,需要将分子和分母转成 $64$ 位整数表示。
将分数转成整数或小数,做法是计算分子和分母相除的结果。可能的结果有三种:整数、有限小数、无限循环小数。
如果分子可以被分母整除,则结果是整数,将分子除以分母的商以字符串的形式返回即可。
如果分子不能被分母整除,则结果是有限小数或无限循环小数,需要通过模拟长除法的方式计算结果。为了方便处理,首先根据分子和分母的正负决定结果的正负(注意此时分子和分母都不为 $0$),然后将分子和分母都转成正数,再计算长除法。
计算长除法时,首先计算结果的整数部分,将以下部分依次拼接到结果中:
如果结果是负数则将负号拼接到结果中,如果结果是正数则跳过这一步;
将整数部分拼接到结果中;
将小数点拼接到结果中。
完成上述拼接之后,根据余数计算小数部分。
计算小数部分时,每次将余数乘以 $10$,然后计算小数的下一位数字,并得到新的余数。重复上述操作直到余数变成 $0$ 或者找到循环节。
如何判断是否找到循环节?注意到对于相同的余数,计算得到的小数的下一位数字一定是相同的,因此如果计算过程中发现某一位的余数在之前已经出现过,则为找到循环节。为了记录每个余数是否已经出现过,需要使用哈希表存储每个余数在小数部分第一次出现的下标。
假设在计算小数部分的第 $i$ 位之前,余数为 $\textit{remainder}i$,则在计算小数部分的第 $i$ 位之后,余数为 $\textit{remainder}{i+1}$。
假设存在下标 $j$ 和 $k$,满足 $j \le k$ 且 $\textit{remainder}j = \textit{remainder}{k+1}$,则小数部分的第 $k+1$ 位和小数部分的第 $j$ 位相同,因此小数部分的第 $j$ 位到第 $k$ 位是一个循环节。在计算小数部分的第 $k$ 位之后就会发现这个循环节的存在,因此在小数部分的第 $j$ 位之前加上左括号,在小数部分的末尾(即第 $k$ 位之后)加上右括号。
[sol1-Java]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| class Solution { public String fractionToDecimal(int numerator, int denominator) { long numeratorLong = (long) numerator; long denominatorLong = (long) denominator; if (numeratorLong % denominatorLong == 0) { return String.valueOf(numeratorLong / denominatorLong); }
StringBuffer sb = new StringBuffer(); if (numeratorLong < 0 ^ denominatorLong < 0) { sb.append('-'); }
numeratorLong = Math.abs(numeratorLong); denominatorLong = Math.abs(denominatorLong); long integerPart = numeratorLong / denominatorLong; sb.append(integerPart); sb.append('.');
StringBuffer fractionPart = new StringBuffer(); Map<Long, Integer> remainderIndexMap = new HashMap<Long, Integer>(); long remainder = numeratorLong % denominatorLong; int index = 0; while (remainder != 0 && !remainderIndexMap.containsKey(remainder)) { remainderIndexMap.put(remainder, index); remainder *= 10; fractionPart.append(remainder / denominatorLong); remainder %= denominatorLong; index++; } if (remainder != 0) { int insertIndex = remainderIndexMap.get(remainder); fractionPart.insert(insertIndex, '('); fractionPart.append(')'); } sb.append(fractionPart.toString());
return sb.toString(); } }
|
[sol1-C#]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| public class Solution { public string FractionToDecimal(int numerator, int denominator) { long numeratorLong = (long) numerator; long denominatorLong = (long) denominator; if (numeratorLong % denominatorLong == 0) { return (numeratorLong / denominatorLong).ToString(); }
StringBuilder sb = new StringBuilder(); if (numeratorLong < 0 ^ denominatorLong < 0) { sb.Append('-'); }
numeratorLong = Math.Abs(numeratorLong); denominatorLong = Math.Abs(denominatorLong); long integerPart = numeratorLong / denominatorLong; sb.Append(integerPart); sb.Append('.');
StringBuilder fractionPart = new StringBuilder(); Dictionary<long, int> remainderIndexDic = new Dictionary<long, int>(); long remainder = numeratorLong % denominatorLong; int index = 0; while (remainder != 0 && !remainderIndexDic.ContainsKey(remainder)) { remainderIndexDic.Add(remainder, index); remainder *= 10; fractionPart.Append(remainder / denominatorLong); remainder %= denominatorLong; index++; } if (remainder != 0) { int insertIndex = remainderIndexDic[remainder]; fractionPart.Insert(insertIndex, '('); fractionPart.Append(')'); } sb.Append(fractionPart.ToString());
return sb.ToString(); } }
|
[sol1-JavaScript]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| var fractionToDecimal = function(numerator, denominator) { if (numerator % denominator == 0) { return '' + Math.floor(numerator / denominator); }
const sb = []; if (numerator < 0 ^ denominator < 0) { sb.push('-'); }
numerator = Math.abs(numerator); denominator = Math.abs(denominator); const integerPart = Math.floor(numerator / denominator); sb.push(integerPart); sb.push('.');
const fractionPart = []; const remainderIndexDic = new Map(); let remainder = numerator % denominator; let index = 0; while (remainder !== 0 && !remainderIndexDic.has(remainder)) { remainderIndexDic.set(remainder, index); remainder *= 10; fractionPart.push(Math.floor(remainder / denominator)); remainder %= denominator; index++; } if (remainder !== 0) { let insertIndex = remainderIndexDic.get(remainder); fractionPart.splice(insertIndex, 0, '('); fractionPart.push(')'); } sb.push(fractionPart.join(''));
return sb.join(''); }
|
[sol1-Golang]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| func fractionToDecimal(numerator, denominator int) string { if numerator%denominator == 0 { return strconv.Itoa(numerator / denominator) }
s := []byte{} if numerator < 0 != (denominator < 0) { s = append(s, '-') }
numerator = abs(numerator) denominator = abs(denominator) integerPart := numerator / denominator s = append(s, strconv.Itoa(integerPart)...) s = append(s, '.')
indexMap := map[int]int{} remainder := numerator % denominator for remainder != 0 && indexMap[remainder] == 0 { indexMap[remainder] = len(s) remainder *= 10 s = append(s, '0'+byte(remainder/denominator)) remainder %= denominator } if remainder > 0 { insertIndex := indexMap[remainder] s = append(s[:insertIndex], append([]byte{'('}, s[insertIndex:]...)...) s = append(s, ')') }
return string(s) }
func abs(x int) int { if x < 0 { return -x } return x }
|
[sol1-C++]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class Solution { public: string fractionToDecimal(int numerator, int denominator) { long numeratorLong = numerator; long denominatorLong = denominator; if (numeratorLong % denominatorLong == 0) { return to_string(numeratorLong / denominatorLong); }
string ans; if (numeratorLong < 0 ^ denominatorLong < 0) { ans.push_back('-'); }
numeratorLong = abs(numeratorLong); denominatorLong = abs(denominatorLong); long integerPart = numeratorLong / denominatorLong; ans += to_string(integerPart); ans.push_back('.');
string fractionPart; unordered_map<long, int> remainderIndexMap; long remainder = numeratorLong % denominatorLong; int index = 0; while (remainder != 0 && !remainderIndexMap.count(remainder)) { remainderIndexMap[remainder] = index; remainder *= 10; fractionPart += to_string(remainder / denominatorLong); remainder %= denominatorLong; index++; } if (remainder != 0) { int insertIndex = remainderIndexMap[remainder]; fractionPart = fractionPart.substr(0,insertIndex) + '(' + fractionPart.substr(insertIndex); fractionPart.push_back(')'); } ans += fractionPart;
return ans; } };
|
[sol1-Python3]1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| class Solution: def fractionToDecimal(self, numerator: int, denominator: int) -> str: if numerator % denominator == 0: return str(numerator // denominator)
s = [] if (numerator < 0) != (denominator < 0): s.append('-')
numerator = abs(numerator) denominator = abs(denominator) integerPart = numerator // denominator s.append(str(integerPart)) s.append('.')
indexMap = {} remainder = numerator % denominator while remainder and remainder not in indexMap: indexMap[remainder] = len(s) remainder *= 10 s.append(str(remainder // denominator)) remainder %= denominator if remainder: insertIndex = indexMap[remainder] s.insert(insertIndex, '(') s.append(')')
return ''.join(s)
|
复杂度分析