ArronHC的博客

Back

题目描述#


给定两个整数,分别表示分数的分子 numerator 和分母 denominator,以 字符串形式返回小数 。

如果小数部分为循环小数,则将循环的部分括在括号内。

如果存在多个答案,只需返回 任意一个 。

对于所有给定的输入,保证 答案字符串的长度小于 10410^4 。

示例 1:

输入: numerator = 1, denominator = 2 输出: “0.5”

示例 2:

输入: numerator = 2, denominator = 1 输出: “2”

示例 3:

输入: numerator = 4, denominator = 333 输出: “0.(012)”

提示:

  • -2^31 <= numerator, denominator <= 2^31 - 1
  • denominator != 0

解题思路#


这道题我们采用长除法的思路来做,这样我们不直接通过浮点数计算,而是一步步将小数部分转换到整数部分,依次得到小数的每一位。

例如: 计算 7 / 4 = 1.75

  • 首先是整数部分:7 / 4 = 1 余 3
    • 记录余数 3
  • 第一位小数:3 * 10=30 , 30/4 = 7 余 2
    • 记录余数 2
  • 第二位小数:2 * 10=20 , 20/4 = 5 余 0 得到结果为 1.75

再例如: 计算 1 / 6 = 0.1666666…

  • 首先是整数部分:1 / 6 = 0 余 1
    • 记录余数 1
  • 第一位小数: 1 * 10 = 10 , 10 / 6 = 1 余 4
    • 记录余数 4
  • 第二位小数: 4 * 10 = 40 , 40 / 6 = 6 余 4
    • 余数 4 出现过,6 是循环节
Note

这种方法相当于每次将小数点往右移一位,以此来得到小数的下一位

因此我们可以总结出以下解题步骤:

1. 整数部分#

这里会有两种情况,

  • 除得尽:则答案就是结果的字符串形式
  • 除不尽:需要继续处理小数部分

2. 小数部分#

同样是两种情况,

  • 通过长除法最终能结束:有限小数,直接把小数部分接上就行
  • 通过长除法结束不了:无限循环小数,需要找循环节

3. 怎么找循环节#

我们来看余数部分,假如说余数在某一次和这一次一样的,后面的结果也一定是一样的,也就是发生了循环。 我们通过哈希表,映射一下余数和对应的位置,从而得到循环节。

4. 需要注意的点#

  • 需要先判断分子分母的正负,看看答案是否需要加负号
  • 因为负数最小到 231-2^{31},取正会导致溢出,所以需要 long long
Tip

在判断结果的正负时,需要讨论分子分母的正负,这里我们可以直接使用异或来解决,异或,同 0 异 1,所以可以这样写:

if(x < 0 ^ y < 0) ans.push_back('-');
cpp

纠结部分#

在判断循环节部分的代码,我之前是这样写的:

//小数部分
        unordered_map<ll,int> index;
        int cnt=0;
        string decimal;
        while(remainder!=0) {
            remainder *= 10;
            int cur = remainder/y;
            remainder %= y;
            if(index.count(remainder)) {//之前有过
                ans += decimal.substr(0,index[remainder]) + "(" + decimal.substr(index[remainder]) + ")";
                return ans;
            }
            index[remainder] = cnt++;
            decimal += (to_string(cur));
        }
        ans += decimal;
cpp

结果在解决 1/6 时发现了问题,因为余数依次为 1、4、4,结果在第二次发现 4 时就终止了,这时 ans 是 0.(1)

Error
  • 首先是整数部分:1 / 6 = 0 余 1
  • 记录余数 4
  • 第一位小数: 1 * 10 = 10 , 10 / 6 = 1 余 4
  • 余数 4 出现过,1 是循环节
Why

因为实际上第一个余数 1 对应的应该是第一位小数,但是这版代码误将下一次的余数和当前的小数点做了对应,自然就不对了

完整代码#


#define ll long long

class Solution {

public:

    string fractionToDecimal(int numerator, int denominator) {

        ll x = numerator;

        ll y = denominator;

        if(x==0) return to_string(0);

        string ans;

        if(x < 0 ^ y < 0) ans.push_back('-');

        x = abs(x),y = abs(y);

        //整数部分

        ll remainder = x % y;

        if(remainder==0) {

            ans += to_string(x/y);

            return ans;

        }else {

            ans += to_string(x/y);

        }

        ans.push_back('.');

  

        //小数部分

        unordered_map<ll,int> index;

        int cnt=0;

        string decimal;

        while(remainder!=0) {

            if(index.count(remainder)) {//之前有过

                ans += decimal.substr(0,index[remainder]) + "(" + decimal.substr(index[remainder]) + ")";

                return ans;

            }

            index[remainder] = cnt++;

            remainder *= 10;

            int cur = remainder/y;

            remainder %= y;

            decimal += (to_string(cur));

        }

        ans += decimal;

        return ans;

    }

};
cpp
每日一题:分数转小数
https://www.arronhc.cyou/blog/%E6%AF%8F%E6%97%A5%E4%B8%80%E9%A2%98%E5%88%86%E6%95%B0%E8%BD%AC%E5%B0%8F%E6%95%B0
Author ArronHC
Published at 2025年9月25日
Comment seems to stuck. Try to refresh?✨