leetcode 166.分数到小数
给定两个整数,分别表示分数的分子 numerator 和分母 denominator,
以字符串形式返回小数。如果小数部分为循环小数,则将循环的部分括在括号内。
示例 1:
输入: numerator = 1, denominator = 2
输出: "0.5"
示例 2:
输入: numerator = 2, denominator = 1
输出: "2"
示例 3:
输入: numerator = 2, denominator = 3
输出: "0.(6)"
思路最重要:
初次分析:
将结果分为整数部分和小数部分,整数部分好求(numerator/denominator
即可)
- 小数的计算过程,小数部分可定义为
StringBuilder decimal = new Stringbuilder()
- 计算
numerator=numerator%denominator
numerator *=10
- 若
numerator=0
- 表明小数部分结束,我们需要返回计算结果
decimal
- 表明小数部分结束,我们需要返回计算结果
- 若
numerator!=0
- 表明小数部分未结束,将
numerator/denominator
添加到小数的计算结果,即decimal.append(numerator/denominator)
;
若numerator/denominator==0
(解决小数部分中会出现0值的情况);
判断是否能在decimal
从后往前查找到值numerator/denominator
,并且numerator/denominator
的不为0(解决循环小数的问题,之所以加一个判断条件最后一个字符不为0,是因为防止出现小数的计算结果出现连续0的情况,如不加出现.00...
时,结果就会矛盾);若能找到,取出循环子串,与()
进行拼接,返回计算结果 - 在小数的循环部分出现0时如何解决?
- 表明小数部分未结束,将
- 继续计算
numerator=numerator%denominator
,开始下一轮循环
- 计算
再次分析:
经过实际实现时的再次分析,发现本题有许多需要注意的地方,但是整体思路还是与初次的分析的思路保持不变。
整数部分的求解:
-
整数部分的求解不能轻视。其一,结果有正负之分,其二,数据是否会溢出需要提前在此进行处理。方案如下:
- 数据扩位:
long numera = numerator; long denomina = denominator;
- 异号修正:
String integerPart = ""; if((numera^denomina)<0){ integerPart += "-"; numera = Math.abs(numera); denomina = Math.abs(denomina); } integerPart += numera/denomina;
促使我进行上述操作的一个例子:
-2147483648/1
小数部分的求解:
- 此步的思路与初次分析的思路差不多,循环的整体架构如下。若被除数numera小于除数denomina,将除数乘以10,在除以被除数即可得到一个小数部分的结果,当被除数numera模除数denomina即整除时,小数部分就结束了。
while((numera=numera%denomina)!=0){
numera = numera*10;
long num = numera/denomina;
...
...
...
}
- 显然,上述的操作可以处理非循环小数,但是循环小数怎么处理呢?这是一个关键。读者可以看到,我的初次分析是以num是否等于0为标准,来确定循环部分,但是当我想到这两个反例时,便放弃了此想法。
反例1:
1/110 = 0.0(09)
反例2:
1/333 = 0.(003)
- 以反例1为例,当计算结果为0.009
0
,此时以最后一个零为界限,利用字符串的decimalPart.toString().lastIndexOf从后往前确定循环部分为(09),结果正确;反例2呢?当计算结果为0.0030
,循环部分就确定为(03),结果错误,利用indexOf从前往后找,两者结果的正确性同样不一致。
我们可以换一种思路去确定循环体,小数部分之所以会有循环体,根本原因是被除数numera在循环中重复出现了(也就是说,numera的重复出现,使得小数部分也重复出现,因此循环)抓住这一点,不攻自破。利用map存储每次得到的被除数numera与decimalPart得到的小数的索引的映射关系,当map.contains(numera)为真时,表明之前循环中出现了numera,由此确定循环部分。
代码不重要:
class Solution {
public String fractionToDecimal(int numerator, int denominator) {
if(numerator==0) return 0+"";
long numera = numerator;
long denomina = denominator;
String integerPart = "";
if((numera^denomina)<0){
integerPart += "-";
numera = Math.abs(numera);
denomina = Math.abs(denomina);
}
integerPart += numera/denomina;
if(numerator%denomina==0) return integerPart;
StringBuilder decimalPart = new StringBuilder(".");
Map<Long,Integer> map = new HashMap<>();
int i=1;
while((numera=numera%denomina)!=0){
numera = numera*10;
long num = numera/denomina;
String tmp = decimalPart.toString();
if(map.containsKey(numera)){
int index = map.get(numera);
return integerPart +tmp.substring(0,index)+"("+tmp.substring(index)+")";
}else{
map.put(numera,i);
}
decimalPart.append(""+num);
i++;
}
return integerPart + decimalPart.toString();
}
}
学习了别人的解法,进行优化后:
class Solution {
public String fractionToDecimal(int numerator, int denominator) {
if(numerator==0) return 0+"";
long numera = numerator;
long denomina = denominator;
StringBuilder res = new StringBuilder();
if((numera^denomina)<0){
res.append("-");
numera = Math.abs(numera);
denomina = Math.abs(denomina);
}
res.append(numera/denomina);
if(numerator%denomina==0) return res.toString();
res.append(".");
Map<Long,Integer> map = new HashMap<>();
while((numera=numera%denomina)!=0){
numera = numera*10;
long num = numera/denomina;
if(map.containsKey(numera)){
res.insert(map.get(numera),"(");
res.append(")");
return res.toString();
}
map.put(numera,res.length());
res.append(""+num);
}
return res.toString();
}
}