力扣每日一题0418-386. 字典序排数

120 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第17天,点击查看活动详情 。

给你一个整数 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<=5104`1 <= n <= 5 * 10^4`

深度优先搜索

题目要求设计一个时间复杂度为 O(n)O(n) 且使用 O(1)O(1) 额外空间的算法,因此我们不能使用直接排序的方法。

那么对于一个整数 number\textit{number},它的下一个字典序整数对应下面的规则:

  • 尝试在 number\textit{number} 后面附加一个零,即 number×10\textit{number} \times 10,如果 number×10n\textit{number} \times 10 \le n,那么说明 number×10\textit{number} \times 10 是下一个字典序整数;
  • 如果 numbermod10=9\textit{number} \bmod 10 = 9number+1>n\textit{number} + 1 \gt n,那么说明末尾的数位已经搜索完成,退回上一位,即 number=number10\textit{number} = \Big \lfloor \dfrac{\textit{number}}{10} \Big \rfloor,然后继续判断直到 numbermod109\textit{number} \bmod 10 \ne 9number+1n\textit{number} + 1 \le n 为止,那么 number+1\textit{number} + 1 是下一个字典序整数。

字典序最小的整数为 number=1\textit{number} = 1,我们从它开始,然后依次获取下一个字典序整数,加入结果中,结束条件为已经获取到 nn 个整数。

var lexicalOrder = function(n) {
    const ret = [];
    let number = 1;
    for (let i = 0; i < n; i++) {
        ret.push(number);
        if (number * 10 <= n) {
            number *= 10;
        } else {
            while (number % 10 === 9 || number + 1 > n) {
                number = Math.floor(number / 10);
            }
            number++;
        }
    }
    return ret;
};

image.png

字典序,找规律,递归

数字的字典序是这样定义的: 单字符比较的顺序是0<1<...<9 多位字符时从前至后按位比较,直到某一位分出大小后停止 上一条分不出大小时,长度短的字典序靠前

根据字典序的定义,以及对样例输出找规律,可以发现当已有数字num时,可以按顺序展开与num相关的后续数字: a, 尾部添加0,即num*10,并递归处理 b, 自身加1,即num+1,并递归处理(当num以9结尾时,跳过这一步)

var lexicalOrder = function(n) {
  return dfs(n)
};

function dfs(target, prev = 0, list = []) {
  for(let i = 0; i < 10; i++){
      if(i === 0 && prev === 0) continue
      let num = prev * 10 + i
      if(num > target) continue
      list.push(num)
      dfs(target, num, list)
  }
  return list
}