Leetcode 386. Lexicographical Numbers

题目

Given an integer n, return all the numbers in the range [1, n] sorted in lexicographical order.

You must write an algorithm that runs in O(n) time and uses O(1) extra space.
来源:力扣(LeetCode)
链接:leetcode 0386

解析

第一眼:嗯,题目很短,来者不善
第二眼:nm,什么是lexicographical order?
第三眼: O ( n ) O(n) O(n)的要求有点变态
好吧又是一道我解不了的medium,折腾一个上午也没想明白怎么才能 O ( n ) O(n) O(n)
下面进入正题:

  • 什么叫做字典序(lexicographical order)
    其实定义很简单,说起来是按照英文字典中的单次的排序,其实简单的理解就是对字符串比较大小然后按照升序排列
  • 怎么做到 O ( n ) O(n) O(n)
    说到字符串排序,想来大家都蠢蠢欲动,直接转成字符串然后一波sort直接干掉。这当然是最直接的解法,但是它能做到 O ( n ) O(n) O(n)吗?并不能!不管你选什么排序算法(什么猴子排序、睡眠排序任你随便选什么排序算法,平均复杂度最好也就做到 O ( N l o g N ) O(NlogN) O(NlogN)),因此题目设计之初应该不是让我们用 s o r t sort sort来解决问题的,那到底该如何解决呢?

题解

我们来看两个示例

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

n = 123
输出:[1, 10, 100, 11, 110, 111, 112, 113, …]

可以看到,这种排序方法其实有点类似于深度优先搜索的操作,即给定一个前缀数字(前缀数字有 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 1,2,3,4,5,6,7,8,9 1,2,3,4,5,6,7,8,9),我们需要把以它开头的所有数据都找出来并给出正确的排序。为什么说像是深度优先排序呢?看下图:
在这里插入图片描述
是不是有点DFS的意思了?那么这道题是否能用DFS来做呢?我们还得考虑,由于递归调用DFS会产生堆栈开销,但是题目要求我们额外的空间开销为 O ( 1 ) O(1) O(1),因此这里不能使用递归DFS,但是迭代DFS总是可以的嘛,上代码:

class Solution:
    def lexicalOrder(self, n: int) -> List[int]:
        # define the result
        result = [0] * n
        # initial value must be 1
        num = 1
        # start to search with dfs
        for i in range(n):
            # put number to result
            result[i] = num
            # num * 10 until the result is greater than n, this is the left most path(in dfs)
            if num * 10 <= n:
                num *= 10
            else:
                # here we need to find the edge case, num % 10 is 9 when the subtree of current prefix is traversed
                # or num + 1 > n means that the current node is the last one to traverse for current prefix
                # both of them means the change of prefix
                while num % 10 == 9 or num + 1 > n:
                	# move upward
                    num //= 10
                # update number
                num += 1
        return result

说实话,看到官解的这个代码的时候,一种优雅的感觉油然而生(自己写的是什么shit…)。很简单,从最左侧开始一条条路走下去,走不动的时候就往上回一节,或者走到一个前缀的子树的尽头了(遇到了尾9)也往上回一步。

次优解

虽然使用递归的解法不是满足题意的解法,这里我们也把这种解法给出来

class Solution:
    def lexicalOrder_dfs(self, n: int) -> List[int]:
        # define the result list
        result = []
        # dfs in recursive format
        def dfs(cur):
            # edge case
            if cur > n:
                return
            # store each result in result
            result.append(cur)
            for i in range(10):
                dfs(cur * 10 + i)
        for i in range(1, 10):
            # dfs for each prefix
            dfs(i)
        return result

总结

字典树的定义已经决定了,该类问题的性质其实就是DFS,本题与leetcode 440 K-th Smallest in Lexicographical Order有着异曲同工之妙。我们将在下一篇文章中讨论该题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值