目录
1. 问题描述
给你一个整数 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
2. 解题分析
将所有小于等于 n 的数字按照字典序的方式,可以构建字典树如下(以1为根节点,然后每个节点i的子节点为 (10*i, 10*i + 1, ..., 10 *i + 9)),这个可以看作是一个10叉树:
构建完这个树后,按照前序遍历即可以得到字典序排序结果。而前序遍历是可以用深度优先搜索的方式得到的。
当然,这个字典树不必显式地构建出来,我们可以直接基于深度优先搜索给出字典序排序结果。基本流程如下:
- 初始化:将0入栈
- 从栈中取出一个数x,加入结果列表
- 将x在字典树上的子节点(10x+9,10x+8,...,10x)中大于等于0小于等于n的数入栈
- 重复以上步骤2,3直到栈变为空
以上有两点细节要注意:
(1)虽然0并不需要,但是从0入栈开始,可以让程序变得简洁一些。最后从结果中要去掉0,而且在取邻节点入栈时,要排除0
(2)邻接点入栈时,是按倒序入栈。比如说1的子节点是按照19,18,...的顺序入栈,这样它们被取出时是按照所希望的顺序出栈
3. 代码实现
import time
from typing import List
from collections import deque
class Solution:
def lexicalOrder(self, n: int) -> List[int]:
q = deque([0])
ans = []
while len(q)>0:
x = q.pop()
ans.append(x)
# for k in range(10):
for k in range(9,-1,-1):
y = 10*x+k
if 0 < y <= n:
q.append(y)
return ans[1:]
if __name__ == "__main__":
sln = Solution()
n = 5*10**4
tstart = time.time()
ans = sln.lexicalOrder(n)
tstop = time.time()
print('n={0}, ans={1}, tcost={2:4.2f}'.format(n,ans,tstop-tstart))
执行用时:140 ms, 在所有 Python3 提交中击败了37.39%的用户
内存消耗:18.6 MB, 在所有 Python3 提交中击败了86.94%的用户
好像这是碰到的第2道字典序的题目,上一次是difficult级别的leetcode440(Leetcode0440. 字典序的第K小数字(difficult,三种算法)),当时没有字典序的概念,死磕了很久最后只好求助于官解。有了上次的铺垫,这次就轻松多了,几分钟搞定。
不过,官解的解法更加高效(运行时间只有上面我的解法的五分之一):
class Solution:
def lexicalOrder(self, n: int) -> List[int]:
ans = [0] * n
num = 1
for i in range(n):
ans[i] = num
if num * 10 <= n:
num *= 10
else:
while num % 10 == 9 or num + 1 > n:
num //= 10
num += 1
return ans