leetcode-166-分數到小數(用余數判斷有沒有出現小數的循環體)


題目描述:

 
給定兩個整數,分別表示分數的分子 numerator 和分母 denominator,以字符串形式返回小數。

如果小數部分為循環小數,則將循環的部分括在括號內。

示例 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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM