整數反轉和迴文數

在開始之前,我覺得應該默唸一遍奧卡姆剃刀原理:“如無必要,勿增實體”。我感覺這一條原則在做算法的時候特別重要:是什麼就是什麼,不應該增加額外的元素。當然,在需要空間換時間(或者空間換時間)的時候,增加輔助元素是有必要的,並不衝突。

爲什麼這麼說呢,且看這兩道題目:

給出一個 32 位的有符號整數,你需要將這個整數中每位上的數字進行反轉。

假設我們的環境只能存儲得下 32 位的有符號整數,則其數值範圍爲 [−2^31, 2^31 − 1]。請根據這個假設,如果反轉後整數溢出,那麼就返回 0。

判斷一個整數是否是迴文數。迴文數是指正序(從左向右)和倒序(從右向左)讀都是一樣的整數;負數不是迴文數。

其實這兩道題從本質上是一樣的,都是對數字的反轉。

但是按照常規思路,第一反應是什麼?我覺得很多人(比如我)都會想到,先把數字變成字符串,然後進行反轉。這個思路看起來很合理,但仔細一想就會覺得不對,因爲明明是數字之間的操作,爲什麼要變成字符串呢?而且在反轉整數的時候,還要對負號進行額外的處理。

/**
 * ------result------
 * memory: 8 MB (90%)
 * speed: 0 ms  (100%)
*/
int reverse(int x) {
    int res = 0;
    while (x != 0) {
        // INT32_MIN / -1會溢出,所以需要轉換成long
        // 至於爲什麼不用res跟INT32_MAX比較,因爲不能確定res的符號,所以範圍可能會錯誤
        if (res != 0 &&
            (long)INT32_MAX / res < 10 &&
            (long)INT32_MIN / res < 10) {
            return 0;
        }
        res = res * 10 + x % 10;
        x /= 10;
    }
    return res;
}

其實這個做法有點取巧的意思,因爲如果環境不支持long,這個做法就會失效;最好的做法是對最後一位進行判斷。另外,我覺得涉及到倒序對數字的每一位進行操作的問題,秦九韶算法(也就是所謂的霍納算法)都是值得考慮的,因爲這個算法可以以O(n)的時間複雜度對每一位進行操作。

至於迴文數,明明判斷一下反轉後的整數和反轉之前是否相等就夠了,爲什麼要引入字符串呢?但是因爲迴文數的特殊性,這題有特殊的解法:只反轉一半,也就是修改一下反轉結束的判斷條件:

bool isPalindrome(int x) {
    // 負數不是迴文數
    // 除了0,不存在最後一位爲0的迴文數
    if (x < 0 ||
        (x % 10 == 0 && x != 0)) {
        return false;
    }
    int res = 0;
    // 如果x <= res,說明已經過了一半了
    while (x > res) {
        res = res * 10 + x % 10;
        x /= 10;
    }
    // 如果是奇數,到這裏還需要額外除以10;因爲過去了一位
    return x == res || x == res / 10;
}

我感覺有時候是不是自己提高了算法題的難度……

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章