順序刷題

okumiko發表於2020-12-04

將一個給定字串根據給定的行數,以從上往下、從左到右進行 Z 字形排列。

比如輸入字串為 "LEETCODEISHIRING" 行數為 3 時,排列如下:

L   C   I   R
E  T  O  E S  I  I  G
E   D   H   N

之後,你的輸出需要從左往右逐行讀取,產生出一個新的字串,比如:"LCIRETOESIIGEDHN"

請你實現這個將字串進行指定行數變換的函式:

string convert(string s, int numRows);

示例 1:

輸入: s = “LEETCODEISHIRING”, numRows = 3
輸出: “LCIRETOESIIGEDHN”

示例 2:

輸入: s = “LEETCODEISHIRING”, numRows = 4
輸出: “LDREOEIIECIHNTSG”
解釋:

L   D   R
E  O E  I I
E C  I H  N
T   S    G

解題思路:

  • 題目理解:

字串 s 是以 Z 字形為順序儲存的字串,目標是按行列印。

numRows 行字串分別為 s_1, s_2,…, s_n,則容易發現:按順序遍歷字串 s 時,每個字元 cZ 字形中對應的 行索引 先從 s_1增大至 s_n,再從 s_n減小至 s_1 …… 如此反覆。

因此,解決方案為:模擬這個行索引的變化,在遍歷 s 中把每個字元填到正確的行 zWord[row]

  • 演算法流程: 按順序遍歷字串 s

zWord[row] += c: 把每個字元 c 填入對應行 s_{row}

row += goingDown: 更新當前字元 c 對應的行索引;

goingDown = - goingDown: 在達到 Z 字形轉折點時,執行反向。

  • 複雜度分析:

    時間複雜度 O(N) :遍歷一遍字串 s
    空間複雜度 O(N) :各行字串共佔用 O(N) 額外空間。

class Solution {
public:
    string convert(string s, int numRows) {
        if (numRows == 1) return s;//注意特判,numRows為1時,不會向下或向上拼接,只輸出一行也就是原字串,後面的程式會失效越界。
        vector<string> zWord(min((int) s.length(), numRows));//防止出現字串長度小於行數的情況,s.length()返回型別是size_t,所以要強制型別轉換一下才能用min
        int goingDown = -1;//行數加一或減一,初始值應為-1,因為第一次迴圈進判斷會取反,這是第一行。
        int row = 0;//從第一行開始
        for (auto &c:s) {//遍歷字串
            zWord[row] += c;//把字元拼接在應該在的那一行的字串上
            if (row == 0 || row == numRows - 1) {//第一行或最後一行就方向調轉,注意是0和numRows-1
                goingDown = -goingDown;//遇到第一行應該為1,最後一行應該為-1
            }
            row += goingDown;//行數逐漸增加或減小,來回振盪
        }
        string ans;
        for (auto &s:zWord) {//從上到下遍歷行
            ans += s;//把z變換後的每一行拼接起來
        }
        return ans;
    }
};

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

示例 1:

輸入: 123
輸出: 321

示例 2:

輸入: -123
輸出: -321

示例 3:

輸入: 120
輸出: 21

注意:

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

解題思路:

作者:wang_ni_ma
連結:leetcode-cn.com/problems/reverse-i...
來源:力扣(LeetCode)

首先我們想一下,怎麼去反轉一個整數?
用棧?
或者把整數變成字串,再去反轉這個字串?
這兩種方式是可以,但並不好。實際上我們只要能拿到這個整數的 末尾數字 就可以了。
12345為例,先拿到5,再拿到4,之後是321,我們按這樣的順序就可以反向拼接處一個數字了,也就能達到 反轉 的效果。
怎麼拿末尾數字呢?好辦,用取模運算就可以了

順序刷題

  1. 12345 % 10 得到5,之後將12345 / 10
  2. 1234 % 10 得到4,再將1234 / 10
  3. 123 % 10 得到3,再將123 / 10
  4. 12 % 10 得到2,再將12 / 10
  5. 1 % 10 得到1,再將1 / 10

這麼看起來,一個迴圈就搞定了,迴圈的判斷條件是x>0
但這樣不對,因為忽略了 負數
迴圈的判斷條件應該是while(x!=0),無論正數還是負數,按照上面不斷的/10這樣的操作,最後都會變成0,所以判斷終止條件就是!=0
有了取模和除法操作,對於像12300這樣的數字,也可以完美的解決掉了。

看起來這道題就這麼解決了,但請注意,題目上還有這麼一句

  • 假設我們的環境只能儲存得下 32 位的有符號整數,則其數值範圍為 [−2^31, 2^31 − 1]

也就是說我們不能用long儲存最終結果,而且有些數字可能是合法範圍內的數字,但是反轉過來就超過範圍了。
假設有1147483649這個數字,它是小於最大的32位整數2147483647的,但是將這個數字反轉過來後就變成了9463847411,這就比最大的32位整數還要大了,這樣的數字是沒法存到int裡面的,所以肯定要返回0(溢位了)。
甚至,我們還需要提前判斷

順序刷題

上圖中,綠色的是最大32位整數
第二排數字中,橘子的是5,它是大於上面同位置的4,這就意味著5後跟任何數字,都會比最大32為整數都大。
所以,我們到最大數的1/10時,就要開始判斷了
如果某個數字大於 214748364那後面就不用再判斷了,肯定溢位了。
如果某個數字等於 214748364呢,這對應到上圖中第三、第四、第五排的數字,需要要跟最大數的末尾數字比較,如果這個數字比7還大,說明溢位了。

對於負數也是一樣的

順序刷題

上圖中綠色部分是最小的32位整數,同樣是在最小數的 1/10時開始判斷
如果某個數字小於 -214748364說明溢位了
如果某個數字等於 -214748364,還需要跟最小數的末尾比較,即看它是否小於8

TIPS

通過包含標頭檔案<limits.h>使用INT_MAXINT_MIN而不用直接打數字,使程式閱讀更直觀。

class Solution {
public:
    int reverse(int x) {
        int ans = 0;
        while (x) {//最後一位加上後x=0
            int temp = x % 10;
            //只需要考慮最後一次是否越界,因為後面相加如果越界了就無法判斷了,所以要在少一位時判斷前n-1位以及最後一位與INT_MAX和INT_MIN前n-1位以及最後一位比較
            if (ans > INT_MAX / 10 || (ans == INT_MAX / 10 && temp > INT_MAX % 10))
                return 0;//前n-1位大了,後面就不用看,如果前n-1位一樣,看最後一位是否溢位
            if (ans < INT_MIN / 10 || (ans == INT_MIN / 10 && temp < INT_MIN % 10))
                return 0;//前n-1位小了,後面就不用看,如果前n-1位一樣,看最後一位是否溢位
            ans = ans * 10 + temp;//溢位返回0,不溢位正常運算
            x /= 10;
        }
        return ans;
    }
};
本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章