一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情。
每日刷题 2021.04.18
- leetcode原题链接:leetcode-cn.com/problems/le…
- 难度:中等
- 方法:迭代、递归
题目
- 给你一个整数
n,按字典序返回范围[1, n]内所有整数。 - 你必须设计一个时间复杂度为
O(n)且使用O(1)额外空间的算法
示例
- 示例1
输入: n = 13
输出: [1,10,11,12,13,2,3,4,5,6,7,8,9]
- 示例2
输入: n = 2
输出: [1,2]
提示
1 <= n <= 5 * 10^4
解题思路
- 调用
sort()函数,就可以解决题目。因为sort函数不传入自定义的方法,默认就是按照每个元素为字符串进行比较。即:字典序排序 - 分析:字典序排序,即:
101 < 11的,因为第二位上0是小于1的。 - 记当前需要返回的区间为
[1, n](左闭右闭区间)
迭代
- 第一种情况:位数: 确定位数,找到当前可以用
0填充的最大位数。记初始值为j = 1,那么每次都都需要执行j * 10 ^ m(m从1开始),直到执行到大于n,停下来。执行:下一种情况- 同理解释:在
j后面加一个0,即:j * 10,如果j * 10 <= n,说明j * 10就是下一个字典序整数
- 同理解释:在
- 回退的概念:为什么中间使用的是
while循环?因为当你的前一个数全部位的0都已经变成了9,就表示这些位都已经遍历过了,就要一直往前找,没有被遍历过的来+1 - 第二种情况:依次给每一位
+1。因为此时的位数已经和n是一样的,那么此时只需要将每一位依次加1处理。- 同理解析:如果
j % 10 == 9 || j + 1 > n,说明末尾的位数已经搜索完成,退回上一位j / 10 = j,然后继续判断直到j % 10 != 9 && j + 1 <= n为止,j + 1就是下一个字典序整数。
- 同理解析:如果
递归
- 可以想象成一个字典树,每个节点都有
9种情况。
使用dfs的方法,字典树
* 对于每一层的遍历,都需要先输出根节点
* 对于每一个叶子节点的前一个节点,都需要打印全部子节点
* 触底返回,打印其他的叶子节点
* 对于每一个节点来说都是类似的,都是需要打印其全部的子节点
* 因为开始节点不能为0,因此是多源dfs,需要从多个入口进入进行dfs
* dfs书写:三步走
* 1. 确定参数和返回值:i(1 ~ 9) 无
* 2. 确定临界条件 深度是n的位数 n / 10 (从0开始) => (优化:当前的数是否大于`n`即可)
* 3. 确定单层的逻辑 将当前的值添加到ans数组中,并遍历其所有的子节点
AC代码
- 迭代的解法
var lexicalOrder = function(n) {
let ans = [];
for(let i = 0, j = 1; i < n; i++) {
ans.push(j);
if(j * 10 <= n){
j = j * 10;
}else {
while(j % 10 == 9 || j + 1 > n) {
j = parseInt(j / 10);
}
j++;
}
}
return ans;
};
- 递归的解法
var lexicalOrder = function(n) {
let len = parseInt(n / 10) + 1;
let dept = 1,str = '', ans = [];
function dfs(cur) {
// 临界条件
if(cur > n) return;
// 将符合的数值填入到数组中
ans.push(cur)
// 遍历其每一个子节点
for(let j = 0; j <= 9; j++) {
dfs(cur * 10 + j)
}
}
for(let i = 1; i <= 9; i++) {
dfs(i);
}
return ans;
};
总结
- 迭代和递归通常都可以相互转换,迭代在空间复杂度上会小于递归,但是递归的做法更容易想清楚。