Medium

题目描述

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

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

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

对于所有给定的输入,保证 答案字符串的长度小于 10^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

提示:

  • 不要被数学吓到,只需应用基础数学知识。还记得如何进行长除法吗?
  • 对 4/9 进行长除法试试,循环部分很明显。现在试试 4/333,你看到模式了吗?
  • 注意一旦余数开始循环,除法结果也会循环。
  • 小心边界情况!列出尽可能多的测试用例来彻底测试你的代码。

解题思路

这道题要求将分数转换为小数字符串,关键是处理循环小数的情况。

核心思路:

  1. 处理符号:先判断结果的正负性,统一转为正数计算
  2. 整数部分:直接除法得到整数部分
  3. 小数部分:使用长除法模拟,关键是检测循环

循环检测原理:

  • 在长除法中,如果某个余数之前出现过,说明开始循环了
  • 使用哈希表记录每个余数第一次出现的位置
  • 当余数重复时,从该位置开始就是循环部分

边界情况处理:

  • 整除情况(余数为0):直接返回,无循环部分
  • 负数:只有一个操作数为负时结果为负
  • 溢出:使用long避免INT_MIN取绝对值溢出
  • 整数结果:分子能被分母整除时无小数部分

算法步骤:

  1. 处理符号和特殊情况
  2. 计算整数部分
  3. 模拟长除法计算小数部分
  4. 使用哈希表检测循环,在循环位置插入括号

时间复杂度主要取决于循环检测,最坏情况下需要遍历所有可能的余数。

代码实现

class Solution {
public:
    string fractionToDecimal(int numerator, int denominator) {
        if (numerator == 0) return "0";
        
        string result;
        
        // 处理符号
        if ((numerator > 0) ^ (denominator > 0)) {
            result += "-";
        }
        
        // 转为正数,防止溢出
        long num = abs((long)numerator);
        long den = abs((long)denominator);
        
        // 整数部分
        result += to_string(num / den);
        num %= den;
        
        if (num == 0) return result;
        
        // 小数部分
        result += ".";
        unordered_map<long, int> remainderMap;
        
        while (num != 0) {
            if (remainderMap.count(num)) {
                int index = remainderMap[num];
                result.insert(index, "(");
                result += ")";
                break;
            }
            
            remainderMap[num] = result.length();
            num *= 10;
            result += to_string(num / den);
            num %= den;
        }
        
        return result;
    }
};
class Solution:
    def fractionToDecimal(self, numerator: int, denominator: int) -> str:
        if numerator == 0:
            return "0"
        
        result = []
        
        # 处理符号
        if (numerator > 0) ^ (denominator > 0):
            result.append("-")
        
        # 转为正数
        num = abs(numerator)
        den = abs(denominator)
        
        # 整数部分
        result.append(str(num // den))
        num %= den
        
        if num == 0:
            return "".join(result)
        
        # 小数部分
        result.append(".")
        remainder_map = {}
        
        while num != 0:
            if num in remainder_map:
                index = remainder_map[num]
                result.insert(index, "(")
                result.append(")")
                break
            
            remainder_map[num] = len(result)
            num *= 10
            result.append(str(num // den))
            num %= den
        
        return "".join(result)
public class Solution {
    public string FractionToDecimal(int numerator, int denominator) {
        if (numerator == 0) return "0";
        
        StringBuilder result = new StringBuilder();
        
        // 处理符号
        if ((numerator > 0) ^ (denominator > 0)) {
            result.Append("-");
        }
        
        // 转为正数,防止溢出
        long num = Math.Abs((long)numerator);
        long den = Math.Abs((long)denominator);
        
        // 整数部分
        result.Append(num / den);
        num %= den;
        
        if (num == 0) return result.ToString();
        
        // 小数部分
        result.Append(".");
        Dictionary<long, int> remainderMap = new Dictionary<long, int>();
        
        while (num != 0) {
            if (remainderMap.ContainsKey(num)) {
                int index = remainderMap[num];
                result.Insert(index, "(");
                result.Append(")");
                break;
            }
            
            remainderMap[num] = result.Length;
            num *= 10;
            result.Append(num / den);
            num %= den;
        }
        
        return result.ToString();
    }
}
var fractionToDecimal = function(numerator, denominator) {
    if (numerator === 0) return "0";
    
    let result = "";
    
    // Handle sign
    if ((numerator > 0) !== (denominator > 0)) {
        result += "-";
    }
    
    // Convert to positive
    let num = Math.abs(numerator);
    let den = Math.abs(denominator);
    
    // Integer part
    result += Math.floor(num / den);
    num %= den;
    
    if (num === 0) return result;
    
    // Fractional part
    result += ".";
    let remainderMap = new Map();
    
    while (num !== 0) {
        if (remainderMap.has(num)) {
            let index = remainderMap.get(num);
            result = result.substring(0, index) + "(" + result.substring(index) + ")";
            break;
        }
        
        remainderMap.set(num, result.length);
        num *= 10;
        result += Math.floor(num / den);
        num %= den;
    }
    
    return result;
};

复杂度分析

复杂度类型分析说明
时间复杂度O(denominator)最坏情况下需要遍历所有可能的余数,余数范围是[0, denominator-1]
空间复杂度O(denominator)哈希表存储余数位置,最多存储denominator个不同余数