[每日一題] 第二十四題:列印從1到最大的n位數

DRose發表於2020-08-10

輸入數字 n,按順序列印出從 1 到最大的 n 位十進位制數。比如輸入 3,則列印出 1、2、3 一直到最大的 3 位數 999。

示例 1:

輸入: n = 1
輸出: [1,2,3,4,5,6,7,8,9]

說明:

  • 用返回一個整數列表來代替列印
  • n 為正整數

方法一:分治演算法/全排列

解題思路:

題目要求列印“從 1 至最大的 n 位數的列表”,因此需要考慮以下兩個問題:

  1. 最大的 n 位數(記為 end)和 位數 n 的關係: 例如最大的 1 位數是 9,最大的 2 位數是 99,最大的 3 位數是 999。則可退出公式:end = 10^n - 1
  2. 大數越界問題: 當 n 較大時,end 會超出 int32 整型的取值範圍,超出取值範圍的數字無法正常儲存,但由於本題要求返回 int 型別陣列,相當於預設所有數字都在 int32 整型取值範圍內,因此不考慮大數越界問題。

因此,只需要定義區間 [1,10^n - 1] 和步長 1,通過 for 迴圈生成結果列表 res 並返回即可。

程式碼

class Solution {
    public int[] printNumbers(int n) {
        int end = (int)Math.pow(10, n) - 1;
        int[] res = new int[end];
        for(int i = 0; i < end; i++)
            res[i] = i + 1;
        return res;
    }
}

複雜度分析

  • 時間複雜度 O(10^n) :生成長度為 10^n 的列表需使用 O(10^n) 時間。
  • 空間複雜度 O(1) :建立列表需使用 O(1) 大小的額外空間(列表作為返回結果,不計入額外空間)

其他知識點

大數列印解法:

實際上,本題的主要考點是大數越界情況下的列印,需要解決以下三個問題:

  1. 表示大數的變數型別:

    • 無論是 short/int/long…… 任意變數型別,數字的取值範圍都是有限的。因此,大數的表示應用字串 String 型別。
  2. 生成數字的字串集:

    • 使用 int 型別時,每輪可通過 +1 生成下個數字,而此方法無法應用至 String 型別。並且,String 型別的數字的進位操作效率較低,例如 999910000 需要從個位到千位迴圈判斷,進位 4 次。
    • 觀察可知,生成的列表實際上是 n 位 0 - 9 的全排列,因此可避開進位操作,通過遞迴生成數字的 String 列表。
  3. 遞迴生成全排列:

    • 基於分支演算法的思想,先固定高位,向低位遞迴,當個位已被固定時,新增數字的字串,例如當 n = 2 時(數字範圍 1 - 99),固定十位為 0 - 9,按順序依次開啟遞迴,固定個位 0 - 9,終止遞迴併新增數字字串。

[每日一題] 第二十四題:列印從1到最大的n位數

class Solution {
    StringBuilder res;
    int count = 0, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public String printNumbers(int n) {
        this.n = n;
        res = new StringBuilder(); // 數字字串集
        num = new char[n]; // 定義長度為 n 的字元列表
        dfs(0); // 開啟全排列遞迴
        res.deleteCharAt(res.length() - 1); // 刪除最後多餘的逗號
        return res.toString(); // 轉化為字串並返回
    }
    void dfs(int x) {
        if(x == n) { // 終止條件:已固定完所有位
            res.append(String.valueOf(num) + ","); // 拼接 num 並新增至 res 尾部,使用逗號隔開
            return;
        }
        for(char i : loop) { // 遍歷 ‘0‘ - ’9‘
            num[x] = i; // 固定第 x 位為 i
            dfs(x + 1); // 開啟固定第 x + 1 位
        }
    }
}

在此方法下,各數字字串被逗號隔開,共同組成長字串,返回的數字集字串如下所示:

輸入:n = 1
輸出:"0,1,2,3,4,5,6,7,8,9"

輸入:n = 2
輸出:"00,01,02,...,10,11,12,...,97,98,99"

輸入:n = 3
輸出:"000,001,002,...,100,101,102,...,997,998,999"

觀察可知,當前的生成方法仍有以下問題:

  1. 諸如 00,01,02,…… 應顯示為 0,1,2,……,即應 刪除高位多餘的 0
  2. 此方法從 0 開始生成,而題目要求 列表從 1 開始

以上兩個問題的解決方法如下:

  1. 刪除高位多餘的 0:

    • 字串左邊界定義: 宣告變數 start 規定字串的左邊界,以保證新增的數字字串 num[start:] 中無高位多餘的 0。例如當 n = 2 時,1 - 9 時 start = 1,10 - 99 時,start = 0。
    • 左邊界 start 變化規律:觀察可知,當輸出數字的所有位都是 9 時,則下個數字需要向更高位進 1,此時左邊界 start 需要減 1(即高位多餘的 0 減少一個)。例如當 n = 3(數字範圍為 1 - 999)時,左邊界 start 需要減 1 的情況有:“009”進位至“010”,“099”進位至“100”。設數字中 9 的個數為 nine,所有位都為 9 的判斷條件可用以下公式表示,n - start = nine
    • 統計 nine 的方法:固定第 x 位時,當 i = 9 則執行 nine = nine + 1,並在回溯前恢復 nine = nine - 1。
  2. 列表從 1 開始

    • 在以上方法的基礎上,新增數字字串前判斷其是否為 “0” ,若為 “0” 則直接跳過。

複雜度分析:

  • 時間複雜度 O(10^n) : 遞迴的生成的排列的數量為 10^n。
  • 空間複雜度 O(n) : 字元列表 num 使用線性大小的額外空間。

程式碼:

正確表示大數 ,以下程式碼的返回值為數字字串集拼接而成的長字串。

class Solution {
    StringBuilder res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public String printNumbers(int n) {
        this.n = n;
        res = new StringBuilder();
        num = new char[n];
        start = n - 1;
        dfs(0);
        res.deleteCharAt(res.length() - 1);
        return res.toString();
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res.append(s + ",");
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}

本題要求輸出 int 型別陣列。為 執行通過 ,可在新增數字字串 ss 前,將其轉化為 int 型別。程式碼如下所示:

class Solution {
    int[] res;
    int nine = 0, count = 0, start, n;
    char[] num, loop = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'};
    public int[] printNumbers(int n) {
        this.n = n;
        res = new int[(int)Math.pow(10, n) - 1];
        num = new char[n];
        start = n - 1;
        dfs(0);
        return res;
    }
    void dfs(int x) {
        if(x == n) {
            String s = String.valueOf(num).substring(start);
            if(!s.equals("0")) res[count++] = Integer.parseInt(s);
            if(n - start == nine) start--;
            return;
        }
        for(char i : loop) {
            if(i == '9') nine++;
            num[x] = i;
            dfs(x + 1);
        }
        nine--;
    }
}

題解來源

作者:jyd
連結:leetcode-cn.com/problems/da-yin-co...
來源:力扣(LeetCode)

來源:力扣(LeetCode)
連結:leetcode-cn.com/problems/da-yin-co...

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章