leetcode必刷——字典序

字典序排数

386. 字典序排数 - 力扣(LeetCode) (leetcode-cn.com)

迭代法实现DFS:

满足了题目的复杂度要求!

首先有n个数,那么肯定需要循环n次来计算每一个位置的字典数到底是多少。
然后我们再用一个x表示在当前次数下应该是哪个字段数

具体地:

  • 如果当前数*10<=n那么我们还可以继续按照10为一个步子继续遍历
  • 如果不能够那么说明我们已经走到了尽头,接下里是回头落到前面各个位置从0-9去填充数据
  • 然后x++,就是每次都在从最小位置写0-9(或者0-n)这几个数。
class Solution {
    public List<Integer> lexicalOrder(int n) {
        List<Integer> res = new ArrayList<>();
        int x = 1;
        while(res.size() < n){
            //优先在该数后面添加0
            while(x <= n){
                res.add(x);
                x *= 10;
            }
            //将上一位回退并进行加一操作
            while(x % 10 == 9 || x > n){
                x /= 10;
            }
            x++;
        }
        return res;
    }
}

复杂度分析:

  • 空间复杂度:O(1)
  • 时间复杂度:O(n),其中n为整数的数目

递归法实现DFS

不满足复杂度要求,递归有栈开销!

  • [1, n]的数按照字典序添加到答案,本质上是对一颗节点数量为 n,形态类似字典树的多阶树进行遍历,根节点为 0,需要被跳过,因此我们可以从树的第二层开始搜索。
  • 树中每个节点的值为其搜索路径所代表的数字,且每个节点有[0, 9]10 个子节点。
class Solution {
    List<Integer> ans = new ArrayList<>();
    public List<Integer> lexicalOrder(int n) {
        dfs(1, n);
        return ans;
    }
    public void dfs(int start, int n){
        for(int i = 0; i <= 9; i++){
            int a = start + i;
            if(a > n || ans.size() >= n){
                return;
            }
            ans.add(a);
            int next = a * 10;
            if(next <= n){
                dfs(next, n);
            }
        }
    }
}
  • 时间复杂度:本质上在搜索一棵节点数量为 n 的多阶树(形态类似于字典树),复杂度为 O(n)

调包法:

class Solution {
    public List<Integer> lexicalOrder(int n) {
        List<Integer> ans = new ArrayList<>();
        for(int i = 1; i <= n; i++){
            ans.add(i);
        }
        Collections.sort(ans, (a, b) ->{
            return String.valueOf(a).compareTo(String.valueOf(b));
        });
        return ans;
    }
}

字典序中第K小的数字

440. 字典序的第K小数字 - 力扣(LeetCode) (leetcode-cn.com)

本以为有了上一题的基础,这题可以AC了,没想到提交之后:40/69

说明还是哪里出问题了。

下面是看了n多题解后个人认为最容易理解的题解:

  • 这里如果直接把for循环的n改成k会超时,故进行优化。

  • 注意到题目无需返回序列,因此可以在上述代码的基础上进行剪枝优化:

    • 通过计算得到某个节点下的子树节点的总数,设为nodes;
      1. 如果nodes比K小,那么这个部分则可以全部跳过,第K小的数肯定不在这些节点里面;
      2. 如果nodes比k大,则从里面找。

    作者:livorth-u
    链接:https://leetcode-cn.com/problems/k-th-smallest-in-lexicographical-order/solution/by-livorth-u-zvxp/

class Solution {
    public int findKthNumber(int n, int k) {
        long cur = 1;
        k--;//减掉k==0的情况,因为只有第一层不是十叉树
        while(k > 0) {
            // 以cur为根的子树节点有nodes个
            int nodes = getNodes(n, cur);
            // 如果个数比k少,那么这个部分都可以直接跳过
            // 这里有等号,主要是因为上面减1了
            if(k >= nodes) {
                // 跳过全部
                k = k -nodes;
                // 往右移一位
                cur++;
            }
            // 如果数量比k多,那么我们要找的结果就一定是以cur下的子节点
            else {
                // 跳过当前结点
                k = k - 1;
                // 往下走一层
                cur = cur * 10;
            }
        }
        return (int)cur;
    }

    // 获得以cur为根结点的子树节点数量
    private int getNodes(int n, long cur) {
        long next = cur + 1;
        long totalNodes = 0;
        //左闭右开
        while(cur <= n) {
            // 一次性求出下一层的节点个数和,要是没满就用n来减,要是满了就用next减
            totalNodes += Math.min(n - cur + 1, next - cur);
            // 进入下一层
            next = next * 10;
            cur = cur * 10;
        }
        return (int)totalNodes;
    }
}

今日推歌

-----《哪里都是你》 队长

我忘掉你的所有
风里雨里一直大步往前走
我又怎么能够忘掉你的温柔
换不同的场景
但哪里都是你
要怎么能忘记 忘记你
所有人都知道I can’t let you go
每个人都知道 I can’t let you go

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

星回昭以烂

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值