leetcode:386. 字典序排数

题目

给你一个整数 n ,按字典序返回范围 [1, n] 内所有整数。
你必须设计一个时间复杂度为 **O(n)** 且使用 **O(1)** 额外空间的算法。

示例:

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

解答 & 代码

解法 0:快排

快排,排序时将整数转为字符串进行比较,就是字典序了。
但是时间复杂度不符合题意。

  1. class Solution {
  2. private:
  3. int partition(vector<int>& nums, int left, int right)
  4. {
  5. int randomIdx = rand() % (right - left + 1) + left;
  6. swap(nums[left], nums[randomIdx]);
  7. int pivot = nums[left];
  8. while(left < right)
  9. {
  10. while(left < right && to_string(nums[right]) >= to_string(pivot))
  11. --right;
  12. nums[left] = nums[right];
  13. while(left < right && to_string(nums[left]) <= to_string(pivot))
  14. ++left;
  15. nums[right] = nums[left];
  16. }
  17. nums[left] = pivot;
  18. return left;
  19. }
  20. void quickSort(vector<int>& nums, int left, int right)
  21. {
  22. if(left >= right)
  23. return;
  24. int pivot = partition(nums, left, right);
  25. quickSort(nums, left, pivot - 1);
  26. quickSort(nums, pivot + 1, right);
  27. }
  28. public:
  29. vector<int> lexicalOrder(int n) {
  30. vector<int> nums(n);
  31. for(int i = 1; i <= n; ++i)
  32. nums[i - 1] = i;
  33. quickSort(nums, 0, n - 1);
  34. return nums;
  35. }
  36. };

复杂度分析:设整数 n

  • 时间复杂度 O(nlogn):快排
  • 空间复杂度 O(n):数组的长度

执行结果:

  1. 执行结果:通过
  2. 执行用时:788 ms, 在所有 C++ 提交中击败了 5.12% 的用户
  3. 内存消耗:10.1 MB, 在所有 C++ 提交中击败了 95.85% 的用户

解法 1:多叉树 DFS 前序遍历

386. 字典序排数 - O(1) 空间复杂度的迭代方法
多叉树版的递归前序遍历
[中等] 386. 字典序排数 - 图1

class Solution {
private:
    vector<int> result;

    // 深度优先遍历十叉树(当前遍历到的节点值为 num)
    void dfs(int num, int n)
    {
        // 递归结束条件如果当前节点值 num > n,则停止遍历
        if(num > n)
            return;

        // 前序位置:存储当前节点值
        result.push_back(num);
        // 遍历当前节点的所有子节点
        for(int next = num * 10; next < num * 10 + 10; ++next)
            dfs(next, n);
    }   
public:
    vector<int> lexicalOrder(int n) {
        // 第一层从 1 开始遍历(第一位不能是 0)
        for(int num = 1; num < 10; ++num)
            dfs(num, n);
        return result;
    }
};

复杂度分析:设整数 n

  • 时间复杂度 O(n):遍历每个数
  • 空间复杂度 O(log n):递归栈深度

执行结果:

执行结果:通过

执行用时:12 ms, 在所有 C++ 提交中击败了 44.26% 的用户
内存消耗:11.6 MB, 在所有 C++ 提交中击败了 35.43% 的用户

解法 2:多叉树迭代前序遍历

迭代进行前序遍历,通常需要设置额外的栈,但这里:“当前搜索的数 num 本身就能反映出每一层递归的状态,例如当前搜索至 123,我们知道当前层遍历至 3,上一层遍历至 2,再上一层遍历至 1。如果我们需要返回上一层,只需要令 num //= 10 即可”。因此不需要设置额外的栈。

class Solution {
private: 
public:
    vector<int> lexicalOrder(int n) {
        vector<int> result;
        int num = 1;
        while(result.size() < n)
        {
            while(num <= n)                        // 不断进入下一层
            {
                result.push_back(num);
                num = num * 10;
            }
            while(num > n || num % 10 == 9)        // 不断返回上一层
                num = num / 10;
            num += 1;                            // 遍历该层下一个数
        }
        return result;
    }
};

复杂度分析:设整数 n

  • 时间复杂度 O(n):遍历每个数
  • 空间复杂度 O(1):

执行结果:

执行结果:通过

执行用时:8 ms, 在所有 C++ 提交中击败了 73.59% 的用户
内存消耗:10.8 MB, 在所有 C++ 提交中击败了 73.68% 的用户