求一批整数中出现最多的个位数字_剑指 Offer 43. 1n整数中1出现的次数 leetcode 剑指offer系列...

本文介绍了一种解决LeetCode剑指Offer系列问题的方法,即求解1到n整数中1出现的次数。通过分析题目,提出了避免超时的策略,即分别统计每一位上的1在多少个数中存在,然后累加结果。详细阐述了不同位数上1的计算方式,并给出了算法的时间和空间复杂度分析。
摘要由CSDN通过智能技术生成
3c968457466cd85d7fceedbc43f13fd0.png点击专辑上方“蓝字”关注我吧

题目难度: 中等

原题链接[1]

今天继续更新剑指 offer 系列, 老样子晚上 6 点 45 分准时更新公众号 每日精选算法题, 大家记得关注哦~ 另外在公众号里回复 offer 就能看到剑指 offer 系列当前连载的所有文章了

题目描述

输入一个整数 n ,求 1 ~ n 这 n 个整数的十进制表示中 1 出现的次数。

例如,输入 12,1 ~ 12 这些整数中包含 1 的数字有 1、10、11 和 12,1 一共出现了 5 次。

  • 1 <= n < 2^31

题目样例

示例

  • 输入:n = 12

  • 输出:5

  • 输入:n = 13

  • 输出:6

题目思考

  1. 可否单独统计每一位上的 1?

解决方案

思路

  • 一个最简单的思路是从 1 到 n 依次遍历, 然后统计各个数字的 1 的数目并累加, 但观察题目数据规模, 最大都到了 2^31, 这个做法肯定会超时
  • 换个思路, 如果我们可以单独统计每一位上的 1 在多少个数中存在, 这样只需要遍历所有位数并累加结果即可
  • 举个例子, 假如 n = 12x45, 现在要统计1~12x45在第 x 位上 1 的数目, 显然这里分为三种情况:
    • x < 1: 此时要想这一位上有 1, 那么前两位的范围只能是 0~11, 而后两位的范围则可以是 0~99, 也即00100, 00101, ..., 11199这么多种可能性, 只考虑该位上的 1, 那么它的数目就是左右取值范围的乘积, 也即 12*100
    • x = 1: 此时显然仍包含第一种情况中的可能性, 但它有额外的部分, 也即前两位是 12, 然后后面的范围是 0~45, 加起来就是12*100 + 1*46个 1
    • x > 1: 此时仍包含第一种情况中的可能性, 但对于前两位是 12 来说, 后面的取值范围就是 0~99 了, 因为12199 < 12x45, 所以加起来就是12*100 + 1*100个 1
  • 综合这三种情况, 就能够计算出每一位上的 1 的数目, 最后累加起来就是总的 1 的数目
  • 注意 x < 1 的数目是所有情况下共享的, 所以可以先计算出这一部分, 然后针对 x = 1x > 1 再额外计算剩余部分, 从而减少代码冗余
  • 下面的代码对必要步骤有详细的解释, 方便大家理解

复杂度

  • 时间复杂度 O(logN)
    • 只需要遍历 n 的每一位, 总位数是 logN
  • 空间复杂度 O(1)
    • 不需要额外空间

代码

class Solution:
    def countDigitOne(self, n: int) -> int:
        # 对每一位进行判断, 左右相乘, 分大于等于小于1三种情况
        res = 0
        # 将数字先转成字符串, 方便对每一位的处理
        s = str(n)
        for i, x in enumerate(s):
            # 对应x<1时的左边部分的取值范围
            # 注意对于最高位而言, 它的左边部分为0, 这是因为最高位不可能小于1, 所以这部分不应该有
            left = 0 if i == 0 else int(s[0:i])
            # 对应x<1时的右边部分的取值范围
            right = 10**(len(s) - i - 1)
            if x '1':
                # 当x<1时, 直接加上分析的左右部分乘积即可
                res += left * right
            elif x == '1':
                # 当x=1时, 需要额外加上右边的计数, 对于例子12x45 (x=1)来说, 就是46
                # 注意如果此时是最低位, 那么额外只有1种可能, 就是上限n本身, 所以最低位1的话只需要加上1
                extra = 1 if i == len(s) - 1 else int(s[i + 1:]) + 1
                res += left * right + extra
            else:
                # 当x>1时, 需要额外加上1*right
                res += (left + 1) * right
        return res

参考资料

[1]

原题链接: https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/

b7165d552927453082385e096bd1b04c.png f78f75ef0b8e87c1831af2ac97b7fb0c.png b7165d552927453082385e096bd1b04c.png 你的每个赞和在看,我都喜欢! efc663b98c32b6e1ad64b1c7bf686c07.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值