Leetcode 233. 数字 1 的个数

  • Leetcode 233. 数字 1 的个数
  • 题目
    • 给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。
    • 0 <= n <= 10 ^ 9
  • 解法
    • 计算 1 在每一位出现的次数,分为 最低位、中间位(非最低、最高)、最高位 1 出现的次数,假设数字为 abcd,如下
      • 最低位:则最低位出现的次数为 [0,abc)每次都出现,且仅有 d 大于 0 则还加上 abc1
      • 中间位:第二位分为左边 [0,ab]、右边 [0,9],[0,ab)均会出现 [0,9],接下来就看 [ab10,ab19] 出现的次数,使用 n-ab09 小于 0 则为 0,大于 10 则为 10,第三位类似
      • 最高位:首先只要大于 1 位就有最高位,然后最高位没有左边,接着仅需要看 [1000,1999] 有哪些包含即可,使用 n-999 小于 0 则为 0,大于 10 则为 10
    • 时间复杂度:O(logn),空间复杂度:O(1)
  • 代码
    /**
     * 计算 1 在每一位出现的次数,分为 最低位、中间位(非最低、最高)、最高位 1 出现的次数,假设数字为 abcd,如下
     *     最低位:则最低位出现的次数为 [0,abc)每次都出现,且仅有 d 大于 0 则还加上 abc1
     *     中间位:第二位分为左边 [0,ab]、右边 [0,9],[0,ab)均会出现 [0,9],接下来就看 [ab10,ab19] 出现的次数,使用 n-ab09 小于 0 则为 0,大于 10 则为 10,第三位类似
     *     最高位:首先只要大于 1 位就有最高位,然后最高位没有左边,接着仅需要看 [1000,1999] 有哪些包含即可,使用 n-999 小于 0 则为 0,大于 10 则为 10
     * 时间复杂度:O(logn),空间复杂度:O(1)
     */
    private int solution(int n) {
        // 最低位 1 出现的次数:假设 abcd,最低位出现的次数为 [0,abc)每次都出现,且仅有 d 大于 0 则还加上 abc1
        int lowestDigitOne = n / 10 + Math.min(n % 10, 1);

        // 中间位 1 出现的次数(非最低、最高)
        int middleDigitOne = 0;

        // abcd/100=ab 求第二位,abcd/1000=a 求第三位
        long nowDivTen = 100;
        for (; nowDivTen <= n; nowDivTen *= 10) {

            // 第二位分为左边 [0,ab]、右边 [0,9]
            long leftAllDigit = n / nowDivTen + 1;
            long rightAllDigit = nowDivTen / 10;
            // [0,ab)均会出现 [0,9]
            middleDigitOne += (leftAllDigit - 1) * rightAllDigit;

            // [ab10,ab19] 出现的次数,使用 n-ab09 小于 0 则为 0,大于 10 则为 10
            long highestAllDigit = (n / nowDivTen * nowDivTen) + (nowDivTen / 10 - 1);
            middleDigitOne += Math.min(Math.max(n - highestAllDigit, 0), rightAllDigit);
        }

        // 最高位 1 出现的次数
        int highestDigitOne = 0;
        if (n > 9) {
            // 假设 abcd,仅需要看 [1000,1999] 有哪些包含即可,使用 n-999 小于 0 则为 0,大于 1000 则为 1000
            long rightAllDigit = nowDivTen / 10;
            long highestAllDigit = nowDivTen / 10 - 1;
            highestDigitOne += Math.min(Math.max(n - highestAllDigit, 0), rightAllDigit);
        }

//        System.out.println(lowestDigitOne + ":" + middleDigitOne + ":" + highestDigitOne);
        return lowestDigitOne + middleDigitOne + highestDigitOne;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值