LeetCode 166. Fraction to Recurring Decimal

229 阅读1分钟

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 - 1
  • denominator != 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;
    }
};