原題地址
https://leetcode.com/problems/fraction-to-recurring-decimal/
題目描述
Given two integers representing the numerator and denominator of a fraction, return the fraction in string format.
給出兩個整數,一個分子一個分母,將該分數轉化爲小數並返回字符串。
If the fractional part is repeating, enclose the repeating part in parentheses.
如果小數部分有循環,將其規約在括號內。
For example,
例如,
Given numerator = 1, denominator = 2, return “0.5”.
給出 numerator = 1, denominator = 2, 返回 “0.5” 1/2=0.5
.
Given numerator = 2, denominator = 1, return “2”.
給出 numerator = 2, denominator = 1, 返回 “2” 2/1=2
.
Given numerator = 2, denominator = 3, return “0.(6)”.
給出 numerator = 2, denominator = 3, return “0.(6)” 1/3=0.(6)
.
解題思路
解題思路比較簡單,就是簡單的除法運算,每次運算獲取商和餘數,直到商爲0或者發現出現循環小數的情況。這裏需要注意的有以下幾點:
- 符號問題,輸入有可能是正數或負數,結果有正有負
- -0問題,分子爲0分母爲負數時,結果是0而不是-0
- 溢出問題,INT_MIN,可以使用[-1/-2147483648]和[-2147483648/-1]兩組數據來驗證是否解決了這個問題
- 循環小數,只把循環的部分放在括號內,可以使用[1/6]驗證是否解決了這個問題
計算過程
0.16
|------
6 / 1.00
0
----
1 0 <-- 被除數=10, 在dividendCache[0]中記錄10.
- 6
----
40 <-- 被除數=40, 在dividendCache[1]中記錄40.
- 36
----
40 <-- 被除數=40, dividendCache[1]中已經記錄過40,因此從第1位小數開始是循環部分.
第1位小數是6,從dividendCache[1]起直到dividendCache[dividendCache.size()-1]的小數是循環部分,應當返回0.1(6)。
總體來說,這個題目並不難,只是各種邊界值可能搞瘋你,oj的平臺也有問題,同樣的代碼在c下過不了,說[-1/-2147483648 = 0.0000000004656612873077392578125]有錯,然而cpp可以通過。另外gcc環境下使用math.h
時,編譯命令需要加入-lm
,例如gcc main.c -o main -lm
。
代碼cpp
class Solution {
public:
const static int CACHE_SIZE = 1000;
/**
* 分數轉換爲小數
* @param numerator 分子
* @param denominator 分母
* @return 字符串
* 如果小數部分是循環小數,用括號把循環部分包括起來
*/
string fractionToDecimal(int numerator, int denominator) {
if (numerator == 0) return "0";
bool isNegative = numerator < 0 ^ denominator < 0; // 結果是否爲負數
/* 處理爲正數 */
double doubleNumerator = fabs(numerator);
double doubleDenominator = fabs(denominator);
double integral = floor(doubleNumerator / doubleDenominator); // 結果的整數部分
doubleNumerator -= doubleDenominator * integral; // 餘數部分
/* 計算並緩存計算結果 */
int len = 0; // 小數部分長度
string cache;// 小數部分緩存
vector<double> dividendCache;
bool isRecurring = false; // 是否是循環小數
int recurringStart = 0; // 循環開始位
int i = 0;
while (doubleNumerator) { // 分子(餘數)不爲0時繼續計算
doubleNumerator *= 10; // 被除數乘10
len = dividendCache.size();
for (i = 0; i < len; ++i) // 查詢是否構成循環(被除數是否已經存在過)
if (dividendCache[i] == doubleNumerator) {
isRecurring = true;
recurringStart = i;
break;
}
if (isRecurring) break; // 如果構成循環,結束計算
i = (int)(floor(doubleNumerator / doubleDenominator)); // 商
cache += '0' + i; // 記錄商
dividendCache.push_back(doubleNumerator); // 記錄被除數
doubleNumerator -= doubleDenominator * i; // 被除數取餘,作爲下次計算的被除數
}
/* 根據緩存數據生成返回數據 */
/* 符號部分 */
string ret = isNegative ? "-" : "";
/* 整數部分 */
string tmp;
char c;
if (integral == 0) ret += "0";
else {
while (integral) {
c = (int)(integral - 10 * floor(integral / 10))+ '0';
tmp = c + tmp;
integral = floor(integral / 10);
}
ret += tmp;
}
/* 小數部分 */
if (dividendCache.size() > 0) ret += '.';
i = 0;
if (isRecurring) {
ret += cache.substr(0, recurringStart);
ret += '(';
ret += cache.substr(recurringStart);
ret += ')';
} else
ret += cache;
return ret;
}
};
完整代碼https://github.com/Orange1991/leetcode/blob/master/166/cpp/main.cpp
測試數據
2 / 1 = 2
-2 / 1 = -2
1 / 2 = 0.5
1 / -2 = -0.5
1 / 3 = 0.(3)
-1 / 3 = -0.(3)
2 / 7 = 0.(285714)
-2 / 7 = -0.(285714)
1 / 11 = 0.(09)
-1 / -11 = 0.(09)
2147483647 / 27 = 79536431.(370)
-2147483647 / 27 = -79536431.(370)
2147483647 / 37 = 58040098.(567)
2147483647 / -37 = -58040098.(567)
1000 / 10 = 100
1000 / -10 = -100
1001 / 10 = 100.1
-1001 / 10 = -100.1
-2147483648 / 1 = -2147483648
-2147483648 / -1 = 2147483648
1 / 214748364 = 0.00(000000465661289042462740251655654056577585848337359161441621040707904997124914069194026549138227660723878669455195477065427143370461252966751355553982241280310754777158628319049732085502639731402098131932683780538602845887105337854867197032523144157689601770377165713821223802198558308923834223016478952081795603341592860749337303449725)
-1 / -2147483648 = 0.0000000004656612873077392578125
1 / -2147483648 = -0.0000000004656612873077392578125
-1 / -2147483648 = 0.0000000004656612873077392578125
1 / 6 = 0.1(6)
2015/8/4