LeetCode 166. Fraction to Recurring Decimal
给定两个整数,分别表示分数的分子 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 - 1denominator != 0
样例解释
样例1中 1/2=0.5,因此输出0.5
样例2中 2/1=1,因此输出1
样例3中 4/333=0.666…,循环体为6,因此输出0.012
算法
(模拟,高精度除法) O(n) 为了方便处理,我们先将所有负数运算转化为正数运算。 然后再算出分数的整数部分,再将精力集中在小数部分。
计算小数部分的难点在于如何判断是否是循环小数,以及找出循环节的位置。 回忆手工计算除法的过程,每次将余数乘10再除以除数,当同一个余数出现两次时,我们就找到了循环节。 所以我们可以用一个哈希表 unordered_map<int,int> 记录所有余数所对应的商在小数点后第几位,当计算到相同的余数时,上一次余数的位置和当前位置之间的数,就是该小数的循环节。
时间复杂度分析:计算量与结果长度成正比,是线性的。所以时间复杂度是 O(n)。
AC 代码
class Solution {
public:
string fractionToDecimal(int _n, int _d) {
long long n = _n, d = _d;
bool minus = false;
if (n < 0) minus = !minus, n = -n;
if (d < 0) minus = !minus, d = -d;
string res = to_string(n / d);
n %= d;
if (!n)
{
if (minus && res != "0") return '-' + res;
return res;
}
res += '.';
unordered_map<long long, int> hash;
while (n)
{
if (hash[n])
{
res = res.substr(0, hash[n]) + '(' + res.substr(hash[n]) + ')';
break;
}
else hash[n] = res.size();
n *= 10;
res += to_string(n / d);
n %= d;
}
if (minus) res = '-' + res;
return res;
}
};