LeetCode 剑指Offer 17 打印从1到最大的n位数
题目
解题
解题参考:面试题17. 打印从 1 到最大的 n 位数(分治算法 / 全排列,清晰图解)
但是面试中一定要考虑大数越界的情况,不然面试官出这道题就显得很奇怪~
解题一:不考虑大数越界
战术性假装不知道要考虑大数问题来个解法先缓缓。
// javascript
var printNumbers = function(n) {
const right_limit = Math.pow(10, n) - 1;
const res = new Array(right_limit);
for (let i = 0; i < right_limit; ++i) {
res[i] = i + 1;
}
return res;
};
解题二:考虑大数越界
注意,最高位在 num 数组的 index 为 0 的位置。
输入: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']
// javascript
var printNumbers = function(n) {
const dfs = (index) => {
// 终止条件:已固定完所有位
if (index === n) {
// 截取 num 并拼接
// n = 3 时, ['0','0','9'] start 为 2 截取 ['9'] s = '9'
// n = 3 时, ['0','9','9'] start 为 1 截取 ['9', '9'] s = '99'
let s = num.slice(start).join('');
// 因为打印数字从 1 开始,所以 ‘0’ 不用存入 res
// 本解法时用大数的思想来解题,题目要求返回整数列表,所以把字符串转换成整数
if (s !== '0') res.push(parseInt(s));
// 比如 n = 3, s = ‘9’ 时, nineCnt = 1, start = 2, 满足条件
// 下一次的 s = ‘10’, start 在这里变成 1
if (n - start === nineCnt) start -= 1;
} else {
for (let i = 0; i < 10; ++i) { // 遍历 0 - 9
if (i === 9) nineCnt += 1;
num[index] = String(i); // 固定第 index 位为 String(i)
dfs(index + 1); // 去固定第 index + 1 位
}
// 举例1: 当前 res 的最后一个数是 9,nineCnt = 1,下一次的数是 10,没有数字 9
// 举例2: 当前 res 的最后一个数是 99,nineCnt = 2,下一次的数是 100,没有数字 9
// dfs(1) 里 nineCnt 回溯成 1,回到 dfs(0) 中 nineCnt 变成 0
nineCnt -= 1;
}
}
const num = new Array(n);
const res = new Array();
let start = n - 1; // n 位数的最低位 index 为 n - 1
let nineCnt = 0; // 9 的个数初始化为 0
dfs(0); // 从最高位开始全排列递归
return res;
};
上面的解法使用 n = nineCnt + start (表示 num 中 [start, n - 1] 范围的每一个字符都是 ‘9’)的规律来更新 start 以删除高位的多余 ‘0’,其间要注意 nineCnt 的增加(+1)和回溯(-1)处理。傻瓜一点的方式就是写一个 removeLeadingZeros
函数来处理。
var printNumbers = function(n) {
const dfs = (index) => {
if (index === n) { // 终止条件:已固定完所有位
let s = removeLeadingZeros(num); // 把高位多余的 ‘0’ 去掉并拼接,把非 0 的数字推进 res
if (s !== '0') res.push(parseInt(s));
} else {
for (let i = 0; i < 10; ++i) { // 遍历 0 - 9
num[index] = String(i); // 固定第 index 位为 String(i)
dfs(index + 1); // 去固定第 index + 1 位
}
}
}
const num = new Array(n);
const res = new Array();
dfs(0); // 从最高位开始全排列递归
return res;
};
const removeLeadingZeros = (num) => {
const n = num.length;
let i = 0;
while (i < n - 1 && num[i] === '0') { // 至少会有一位,所以 i < n - 1
i++;
}
return num.slice(i).join('');
};