166. 分数到小数
给定两个整数,分别表示分数的分子 numerator
和分母 denominator
,以 字符串形式返回小数 。
如果小数部分为循环小数,则将循环的部分括在括号内。
如果存在多个答案,只需返回 任意一个 。
对于所有给定的输入,保证 答案字符串的长度小于 104
。
示例 1:
输入:numerator = 1, denominator = 2
输出:"0.5"
示例 2:
输入:numerator = 2, denominator = 1
输出:"2"
示例 3:
输入:numerator = 2, denominator = 3
输出:"0.(6)"
示例 4:
输入:numerator = 4, denominator = 333
输出:"0.(012)"
示例 5:
输入:numerator = 1, denominator = 5
输出:"0.2"
解题思路
模拟手动除法的运算。
在小数部分, 同样模拟手动除法的算法, 记录所有余数到HashMap里, 如果发生某个余数已经在HashMap存在了, 就认为是已经出现了无限循环, 需要将这部分用括号圈起来。
public String fractionToDecimal(int numerator, int denominator) {
// 0 除以任何数都是 0
if (numerator == 0) {
return "0";
}
// 转成 long, 绕开整形溢出陷阱
long dividend = numerator, divisor = denominator;
boolean negitive = false;
// 判断符号, 同正同负才是正, 否则是负
if (numerator >= 0 ^ denominator >= 0) {
negitive = true;
}
// 转为正数运算
dividend = Math.abs(dividend);
divisor = Math.abs(divisor);
StringBuilder ans = new StringBuilder();
// 负数
if (negitive) {
ans.append("-");
}
// 计算整数部分
long quotient = dividend / divisor;
// 加入整数部分
ans.append(quotient);
// 计算当前余数
dividend = dividend % divisor;
// 没有余数, 说明可以除尽, 没有小数部分
if (dividend == 0) {
return ans.toString();
}
int idx = -1, curPos = 0;
Map<Long, Integer> map = new HashMap<>();
// 下面是除法的手动模拟计算
StringBuilder builder = new StringBuilder();
while (dividend != 0) {
// 余数 * 10 即为当前被除数
dividend *= 10;
// 如果当前被除数之前已经出现过了, 那么代表循环开始
if (map.containsKey(dividend)) {
// 获得循环节的位置
idx = map.get(dividend);
break;
}
// 更新 map
map.put(dividend, curPos);
// 记录当前结果
builder.append(dividend / divisor);
// 计算当前余数
dividend %= divisor;
// 更新小数指针位置
curPos++;
}
String decmical = builder.toString();
// 如果 map 中没有元素, 代表没有小数部分
if (map.size() != 0) {
ans.append(".");
}
// 存在循环节就加入括号, 否则直接加入小数部分
if (idx != -1) {
ans.append(decmical, 0, idx);
ans.append("(");
ans.append(decmical.substring(idx));
ans.append(")");
} else {
ans.append(decmical);
}
return ans.toString();
}