剑指offer_43_1~n整数中出现 1 的次数
1~n 整数中1出现的次数
题目链接:https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/
题目内容:
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
示例 1:
输入:n = 12
输出:5
示例 2:
输入:n = 13
输出:6
限制:
- 1 <= n < 2^31
题目解析
题目解析内容来自于题解中Krahets
解题思路:
将 1 ~ n 的个位,十位,百位、... 的 1 出现的次数相加,即为 1 出现的次数。
设数字 n 是个 x 位数,记 n 的第 i 位为 ni ,则可将 n 写为 nxnx-1... n2n1:
- 称 "ni"为 当前位,记为 cur
- 将 "ni-1ni-2...n2n1"称为 低位,记为 low
- 将 "nxnx-1...ni+2ni+1"称为 高位,记为 high
- 将 10i称为 位因子,记为 dight
某位中 1 出现次数的计算方法:
根据当前位 cur 值的不同,分为以下三种情况:
- 当 cur = 0 时: 此位 1 的出现次数只由高位 high 决定,计算公式为:
high $\times$ digit
如下图所示,以 n = 2304 为例,求 digit = 10(即十位)的 1 出现次数。
- 当 cur=1 时: 此位 1 的出现次数由高位 high 和低位 low 决定,计算公式为:
high $\times$ digit + low + 1
如下图所示,以 n = 2314n=2314 为例,求 digit = 10digit=10 (即十位)的 11 出现次数。
- 当 cur = 2, 3, $\cdots$, 9 时: 此位 1 的出现次数只由高位 high 决定,计算公式为:
(high + 1) $\times$ digit
如下图所示,以 n = 2324 为例,求 digit = 10(即十位)的 1 出现次数。
变量递推公式:
设计按照 “个位、十位、...” 的顺序计算,则 high / cur / low / digithigh/cur/low/digit 应初始化为:
high = n // 10
cur = n % 10
low = 0
digit = 1 # 个位
因此,从个位到最高位的变量递推公式为:
while high != 0 or cur != 0: # 当 high 和 cur 同时为 0 时,说明已经越过最高位,因此跳出
low += cur * digit # 将 cur 加入 low ,组成下轮 low
cur = high % 10 # 下轮 cur 是本轮 high 的最低位
high //= 10 # 将本轮 high 最低位删除,得到下轮 high
digit *= 10 # 位因子每轮 × 10
复杂度分析:
- 时间复杂度 O(logn) : 循环内的计算操作使用 O(1) 时间;循环次数为数字 n 的位数,即 $\log_{10}{n}$ ,因此循环使用 O($\log n$) 时间。
- 空间复杂度 O(1) : 几个变量使用常数大小的额外空间。
代码:
class Solution:
def countDigitOne(self, n: int) -> int:
digit, res = 1, 0
high, cur, low = n // 10, n % 10, 0
while high != 0 or cur != 0:
if cur == 0: res += high * digit
elif cur == 1: res += high * digit + low + 1
else: res += (high + 1) * digit
low += cur * digit
cur = high % 10
high //= 10
digit *= 10
return res