題目描述:
如果小數部分為循環小數,則將循環的部分括在括號內。
示例 1:
輸入: numerator = 1, denominator = 2
輸出: "0.5"
示例 2:
輸入: numerator = 2, denominator = 1
輸出: "2"
示例 3:
輸入: numerator = 2, denominator = 3
輸出: "0.(6)"
要完成的函數:
string fractionToDecimal(int numerator, int denominator)
說明:
1、這道題給定兩個整數,要求將這兩個整數相除的結果存儲在string中,最后返回string。
如果是無限循環小數,則要求把循環的部分用括號括起來。
2、兩個整數相除,結果只有兩種可能,一種是有限循環小數,一種是無限循環小數,不可能出現無限不循環小數。
這道題筆者陷入了幾個誤區,在這里一一列舉一下,可能也會有同學跟筆者犯一樣的錯誤。
①看到2/3=0.6666666……,2/7=0.2857142857142857……,3/7=0.4285714285714286……,就以為所有的循環部分都在小數點后最開始出現。
其實不然,比如1/6=0.166666666666666……,循環部分從第二位開始,我們存儲在string中也應該是0.1(6)。
②結合了①的錯誤,產生了新的想法,判斷當前這一位有沒有出現過,如果有出現過了,那么之前出現的位置開始,到當前位置的前一位,就是循環體。
如果沒有出現過,那么繼續記錄下去,直到出現了重復的或者直接跑完了所有小數部分(有限循環小數)。
但這樣還是錯誤的,因為其實出現重復的位不代表這個時候就開始循環了,比如1315/10000=0.1315,第二個1出現的時候,仍然不是循環。
如果按照上面所說的方法,這時候出現了重復的位,最終結果是0.(13)。
所以究竟循環體出現的標志是什么?我們研究一下1/6。
最開始補零,變成10/6,寫成0.1,這時候余數是4。
余數4再去除以6,變成40/6,寫成0.16,這時候余數是4,。
余數4再去除以6……
這個時候我們都知道接下來必定是循環體結構了,因為出現了相同的被除數。
所以我們不能把兩個整數變成double類型,直接相除,而是應該不斷地整數相除,記錄余數,余數再去除以除數。
在這個過程中記錄余數,如果出現了重復的余數,那么必定是循環體結構了。
③邊界條件,比如-2147483648/-1,-1/-2147483648,7/-12等等。
在下面的代碼中再詳解。
如下為代碼:(附詳解)
string fractionToDecimal(int numerator, int denominator) { if(numerator==INT_MIN&&denominator==-1)//邊界條件,沒法直接除,因為除完結果溢出 return "2147483648"; if(numerator==-1&&denominator==INT_MIN)//邊界條件,都是int類型,沒法除 return "0.0000000004656612873077392578125"; int shang=numerator/denominator,yushu=numerator%denominator;//記錄商和余數 string res;//最終要返回的string if(double(numerator)/double(denominator)<0)//如果兩個數一正一負 { if(shang==0)//如果商為0 res='-'+to_string(abs(shang));//可能有的同學疑惑為什么要這樣處理,比如7/-12,除完shang為0,但是我們要的是-0 else res=to_string(shang);//如果不為0,那么直接處理 } else//如果都是正數或者都是負數 res=to_string(shang);//直接處理 if(yushu==0)//如果余數為0,那么到此為止,返回res就可以了 return res; res+='.';//如果還有余數,那么要加個小數點 unordered_map<int,int>record;//記錄出現過的余數和余數除以除數得到的商的位置 while(yushu!=0) { yushu=abs(yushu);//余數有可能是負的,全都轉為正數 denominator=abs(denominator);//除數也轉為正數 yushu*=10;//余數乘10,作為新的被除數 if(record.count(yushu))//如果之前出現過了這個余數,那么可以取出循環體了 { int start=record[yushu],end=res.size()-1;//start和end表示循環體的開端和末尾 res=res.substr(0,start)+'('+res.substr(start,end-start+1)+')';//加一下括號 return res;//直接返回 } record[yushu]=res.size();//如果沒出現過,那么記錄在record中,value是這個余數除以除數得到的商應該放的位置 shang=yushu/denominator;//更新商 yushu=yushu%denominator;//更新余數 res+=to_string(shang);//加入最新的商 } return res;//如果一直沒有出現重復的余數,那么最終跳出循環后直接返回res }
上述代碼實測0ms,beats 100.00% of cpp submissions。