首頁 > 軟體

Go和Java演演算法詳析之分數到小數

2022-08-11 18:01:54

分數到小數

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

如果小數部分為迴圈小數,則將回圈的部分括在括號內。

如果存在多個答案,只需返回 任意一個 。

對於所有給定的輸入,保證 答案字串的長度小於 104 。

  • 範例 1:

輸入:numerator = 1, denominator = 2

輸出:"0.5"

  • 範例 2:

輸入:numerator = 2, denominator = 1

輸出:"2"

  • 範例 3:

輸入:numerator = 4, denominator = 333

輸出:"0.(012)"  

提示:

-231 <= numerator, denominator <= 231 - 1

denominator != 0

方法一:模擬豎式計算(Java)

這是一道模擬豎式計算(除法)的題目。

首先可以明確,兩個數相除要麼是「有限位小數」,要麼是「無限迴圈小數」,而不可能是「無限不迴圈小數」。

將分數轉成整數或小數,做法是計算分子和分母相除的結果。可能的結果有三種:整數、有限小數、無限迴圈小數。

如果分子可以被分母整除,則結果是整數,將分子除以分母的商以字串的形式返回即可。

如果分子不能被分母整除,則結果是有限小數或無限迴圈小數,需要通過模擬長除法的方式計算結果。為了方便處理,首先根據分子和分母的正負決定結果的正負(注意此時分子和分母都不為 00),然後將分子和分母都轉成正數,再計算長除法。

一個顯然的條件是,如果本身兩數能夠整除,直接返回即可;

如果兩個數有一個為“負數”,則最終答案為“負數”,因此可以起始先判斷兩數相乘是否小於 00,如果是,先往答案頭部追加一個負號 -;

兩者範圍為 int,但計算結果可以會超過 int 範圍,考慮 numerator = -2^{31}和 denominator = -1的情況,其結果為 2^{31},超出 int 的範圍 [-2^{31}, 2^{31} - 1]。因此起始需要先使用 long 對兩個入參型別轉換一下。

class Solution {
    public String fractionToDecimal(int numerator, int denominator) {
        // 轉 long 計算,防止溢位
        long a = numerator, b = denominator;
        // 如果本身能夠整除,直接返回計算結果
        if (a % b == 0) return String.valueOf(a / b);
        StringBuilder sb = new StringBuilder();
        // 如果其一為負數,先追加負號
        if (a * b < 0) sb.append('-');
        a = Math.abs(a); b = Math.abs(b);
        // 計算小數點前的部分,並將餘數賦值給 a
        sb.append(String.valueOf(a / b) + ".");
        a %= b;
        Map<Long, Integer> map = new HashMap<>();
        while (a != 0) {
            // 記錄當前餘數所在答案的位置,並繼續模擬除法運算
            map.put(a, sb.length());
            a *= 10;
            sb.append(a / b);
            a %= b;
            // 如果當前餘數之前出現過,則將 [出現位置 到 當前位置] 的部分摳出來(迴圈小數部分)
            if (map.containsKey(a)) {
                int u = map.get(a);
                return String.format("%s(%s)", sb.substring(0, u), sb.substring(u));
            }
        }
        return sb.toString();
    }
}

時間複雜度:O(M)

空間複雜度:O(M)

方法一:模擬豎式計算(Go)

具體的方法詳情已經在上文中表述,詳情請看上文。

func fractionToDecimal(numerator, denominator int) string {
    if numerator%denominator == 0 {
        return strconv.Itoa(numerator / denominator)
    }

    s := []byte{}
    if numerator < 0 != (denominator < 0) {
        s = append(s, '-')
    }

    // 整數部分
    numerator = abs(numerator)
    denominator = abs(denominator)
    integerPart := numerator / denominator
    s = append(s, strconv.Itoa(integerPart)...)
    s = append(s, '.')

    // 小數部分
    indexMap := map[int]int{}
    remainder := numerator % denominator
    for remainder != 0 && indexMap[remainder] == 0 {
        indexMap[remainder] = len(s)
        remainder *= 10
        s = append(s, '0'+byte(remainder/denominator))
        remainder %= denominator
    }
    if remainder > 0 { // 有迴圈節
        insertIndex := indexMap[remainder]
        s = append(s[:insertIndex], append([]byte{'('}, s[insertIndex:]...)...)
        s = append(s, ')')
    }

    return string(s)
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

時間複雜度:O(M)

空間複雜度:O(M)

總結

到此這篇關於Go和Java演演算法詳析之分數到小數的文章就介紹到這了,更多相關Go Java分數到小數內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com