leetcode-166. 分数到小数

218 阅读3分钟
import java.util.HashMap;
import java.util.Map;

/**
 * ref:https://leetcode-cn.com/problems/fraction-to-recurring-decimal/
 * 给定两个整数,分别表示分数的分子numerator和分母denominator,以字符串形式返回小数。
 * 如果小数部分为循环小数,则将循环的部分括在括号内。如果存在多个答案,只需返回任意一个。
 * 对于所有给定的输入,保证答案字符串的长度小于104。
 * 输入:numerator = 2, denominator = 3
 * 输出:"0.(6)"
 * 输入:numerator = 2, denominator = 1
 * 输出:"2"
 * 输入:numerator = 6, denominator = 50
 * 输出:"0.12"
 */
public class FractionDecimal {

    public static void main(String[] args){
        System.out.println(fractionToDecimal(2,3));
        System.out.println(fractionToDecimal(2,1));
        System.out.println(fractionToDecimal(6,50));
    }

    /**
     * 思路:
     * 首先对分子和分母进行判断,如果分子是0,则输出“0”,如果分母是0,则输出“”。
     * 然后判断,是否可以整除,如果可以整除,则直接使用字符串返回。
     * 如果不可以整除,则需要分两种场景考虑,小数部分可以除尽和不可以除尽。
     * 针对除尽的,比如6/50,先计算整数部分,6/50的整数部分为0,得到整数部分后,将其转换为字符串并加上小数点。
     * 然后对余数6*10=60,在进行与50进行除,得到60/50=1计入字符串中,此时余数为10。
     * 然后对余数10再次进行重复处理,10*10=100,对100/50=2的值计入字符串。
     * 直到两个数取模是0,也就是整除了结束。
     * 针对除不尽的,比如2/3,先计算整数部分,2/3的整数部分为0,得到整数部分后,将其转换为字符串并加上小数点。
     * 然后对余数2*10=20,在进行对3进行除,得到6计入字符串中。对余数2再次进行重复处理,2*10=20,
     * 对20/3=8的值计入字符串,此时发现每次余数都是固定的值2,则表示是循环除不尽。
     * 除计算过程中,将余数和当前字符串长度进行缓存。当下次出现余数相同时,直接从缓存中获取字符串长度值进行插入括号。
     * 代码逻辑,是对上述思路进行了整理后的合并
     * @param numerator
     * @param denominator
     * @return
     */
    public static String fractionToDecimal(int numerator, int denominator) {
        // 对除数和被除数是0的判断
        if(numerator==0) return "0";
        if(denominator==0) return "";
        // 正负数判断,如果有一个数是负数,则结果添加负号
        StringBuilder result = new StringBuilder();
        if((numerator>0&&denominator<0)||(numerator<0&&denominator>0)){
            result.append("-");
        }
        // 先取绝对值
        numerator = Math.abs(numerator);
        denominator = Math.abs(denominator);
        // 记录整数部分
        result.append(numerator/denominator);
        // 取模判断是否可以整除
        int modNum = numerator%denominator;
        // 模值不是0,表示没有整除,肯定是有小数的,需要添加小数点
        if(modNum!=0){
            result.append(".");
        }
        // cache余数对应的字符串长度
        Map<Integer, Integer> modMap = new HashMap<>();
        // 开始计算小数部分
        while(modNum!=0){
            // 缓存当前余值对应的字符串在进行当前取余之前的长度,用于当整除不尽时,每次余数相同时,添加(使用。
            modMap.put(modNum,result.length());
            // 计算本轮数除进行除数据后的值,乘以10是用于进位
            int currentMod = modNum * 10;
            result.append(currentMod/denominator);
            // 从新计算本轮值对除数的余数,如果余数在map中存在,则表示已循环。使用()
            modNum = currentMod%denominator;
            if(modMap.get(modNum)!=null){
                return result.insert(modMap.get(modNum),"(").append(")").toString();
            }
        }
        return result.toString();
    }
}