在问 · 算法题解

386. 字典序排数

中等数学迭代模拟前序遍历力扣原题 ↗

题目描述

输入一个整数 n。把 1 到 n 的所有整数当作字符串,按字典序从小到大排列。

返回排好后的整数列表。

要求时间复杂度 O(n),除返回结果外只用 O(1) 额外空间。

示例 1
输入:n = 13
输出:[1,10,11,12,13,2,3,4,5,6,7,8,9]

解释:按字符串比较,"10" 到 "13" 都排在 "1" 之后、"2" 之前。

示例 2
输入:n = 2
输出:[1,2]

数据范围

  • 1 <= n <= 5 * 10^4

解题思路:迭代模拟前序遍历

把 1..n 想成一棵十叉树:每个数 x 的孩子是 x0..x9。字典序就是这棵树的前序遍历。不用递归,只靠一个变量 cur 模拟:能往下走(cur * 10 ≤ n)就走到第一个孩子;否则尝试走到右兄弟 cur + 1,如果 cur 末位是 9 或 cur + 1 超过 n,就先回到父亲(cur //= 10),直到可以加 1 为止。

为什么正确

第 k 次循环开始时,cur 恰好是字典序中第 k 个数。

复杂度分析

时间 O(n)(每次回退总次数被前进次数摊还),空间 O(1)(不计输出)。

易错点

回退条件有两个:末位是 9(如 19 的下一个是 2 而不是 20),或 cur + 1 超过 n。用 while 而不是 if,因为可能连续回退多层(如 n=199 时 199 → 2)。

参考代码

Python、Java、C++、Go、JavaScript、TypeScript 六种语言,逻辑一致。

class Solution:
    def lexicalOrder(self, n: int) -> List[int]:
        res = []
        cur = 1
        for _ in range(n):
            res.append(cur)
            if cur * 10 <= n:
                cur *= 10
            else:
                while cur % 10 == 9 or cur + 1 > n:
                    cur //= 10
                cur += 1
        return res

载入编辑器,自己跑一遍 →

其他解法

两种方法都按字典树前序遍历输出:深度优先搜索用递归维护路径,主解法用乘 10、加 1、除 10 直接在数字上移动,省掉了栈。

解法 2:字典树深度优先搜索 时间 O(n),空间 O(log n)

把 1 到 n 看成一棵十叉字典树:节点 x 的孩子是 10x 到 10x + 9。从根的孩子 1 到 9 依次出发做前序遍历,访问节点时把它加入答案,再按 0 到 9 的顺序递归孩子,遇到超过 n 的节点就停止。前序遍历的顺序就是字典序。

直接体现“字典序 = 字典树前序遍历”,最容易理解和证明。需要递归栈,深度为数字位数 O(log n);主解法把这次遍历改写成迭代,额外空间 O(1)。

class Solution:
    def lexicalOrder(self, n: int) -> List[int]:
        res = []

        def dfs(x):
            if x > n:
                return
            res.append(x)
            for d in range(10):
                if x * 10 + d > n:
                    break
                dfs(x * 10 + d)

        for x in range(1, 10):
            dfs(x)
        return res

数学 专题的其他题目

查看 数学 全部题目 →