「打印 1 到最大的 n 位數」是經典面試題(劍指 Offer 題 17)。本質是生成所有長度為 n 的十進制字符串(允許前導零),並去除前導零後輸出。本文詳細對比數組遞歸法與字符串模擬加法法兩種核心思路,並給出可直接運行的完整代碼。
將 n 個位置視為 n 個槽,每個槽填 0~9,遞歸枚舉所有組合,最後去除前導零打印。
void dfs(int* num, int n, int index) {
if (index == n) { // 填滿 n 位
printNumber(num, n); // 去除前導零輸出
return;
}
for (int i = 0; i <= 9; ++i) {
num[index] = i;
dfs(num, n, index + 1);
}
}
#include <iostream>
#include <cstring>
using namespace std;
void printNumber(int* num, int n) {
int i = 0;
while (i < n && num[i] == 0) ++i; // 跳過前導零
if (i == n) return; // 全零不打印(即 0)
for (; i < n; ++i) cout << num[i];
cout << '\n';
}
void dfs(int* num, int n, int index) {
if (index == n) {
printNumber(num, n);
return;
}
for (int i = 0; i <= 9; ++i) {
num[index] = i;
dfs(num, n, index + 1);
}
}
void print1ToMaxOfNDigits(int n) {
if (n <= 0) return;
int* num = new int[n];
memset(num, 0, n * sizeof(int));
dfs(num, n, 0);
delete[] num;
}
int main() {
print1ToMaxOfNDigits(3); // 打印 1~999
return 0;
}
維護一個長度為 n 的字符數組初始化為全 '0',反覆執行 +1 操作,直到最高位產生進位溢出為止。
// 返回 true 表示溢出(已達最大 n 位數),false 表示繼續
bool increment(char* num, int n) {
int carry = 1;
for (int i = n - 1; i >= 0; --i) {
int sum = num[i] - '0' + carry;
num[i] = '0' + (sum % 10);
carry = sum / 10;
}
return carry == 1; // 最高位仍有進位 → 溢出
}
#include <iostream>
#include <cstring>
using namespace std;
void printNumber(const char* num, int n) {
int i = 0;
while (i < n && num[i] == '0') ++i;
if (i == n) return;
for (; i < n; ++i) cout << num[i];
cout << '\n';
}
bool increment(char* num, int n) {
int carry = 1;
for (int i = n - 1; i >= 0; --i) {
int sum = num[i] - '0' + carry;
num[i] = '0' + (sum % 10);
carry = sum / 10;
}
return carry == 1;
}
void print1ToMaxOfNDigits_2(int n) {
if (n <= 0) return;
char* num = new char[n + 1];
memset(num, '0', n);
num[n] = '\0';
while (true) {
printNumber(num, n);
if (increment(num, n)) break; // 溢出結束
}
delete[] num;
}
int main() {
print1ToMaxOfNDigits_2(2); // 打印 1~99
return 0;
}
O(10ⁿ),常數因子略大於遞歸法。| 維度 | 遞歸全排列 | 字符串模擬加法 |
|---|---|---|
| 時間複雜度 | Θ(10ⁿ) | Θ(10ⁿ) |
| 空間複雜度 | O(n) 棧 + O(n) 數組 | O(n) 數組 |
| 輸出順序 | 字典序(等效數值序) | 嚴格數值遞增序 |
| 最大可行 n | 受限於棧深度 (~10⁴) | 受限於輸出量與時間 |
| 代碼複雜度 | 低 | 中 |
| 擴展性 | 易擴展為全排列/組合問題 | 易擴展為大數四則運算 |
next_permutation 或手寫迭代版避免棧溢出。ofstream 緩衝寫入文件而非 cout。printNumbers(n) 返回 vector<int>,n ≤ 9 時可直接用 int 存儲,無需大數模擬。「打印 1 到最大 n 位數」考察的核心是大數表示與操作。遞歸法勝在思路清晰、代碼短,適合面試現場白板寫代碼;字符串加法法勝在無遞歸風險、輸出順序天然正確,適合工程落地。實際工作中,n > 6 幾乎不會要求全量打印(輸出量 GB 級),更多是考察大數加法、乘法模擬能力。掌握兩種思路,舉一反三,才是這道題的真正價值。