1 题目描述
2 题解
解题思路: 将1~n 的个位、十位、百位、…的1出现次数相加,即为1出现的总次数。
设数字n是个x位数,记n的第i位为 n i n_i ni,则可将n写为 n x n x − 1 . . . n 2 n 1 n_xn_{x-1}...n_2n_1 nxnx−1...n2n1:
- 称" n i n_i ni"为当前位,记为cur
- 将" n i − 1 n i − 1 . . . n 2 n 1 n_{i-1}n_{i-1}...n_2n_1 ni−1ni−1...n2n1"称为低位,记为low
- 将" n x n x − 1 . . . n i + 2 n i + 1 n_xn_{x-1}...n_{i+2}n_{i+1} nxnx−1...ni+2ni+1"称为高位,记为high
- 将 1 0 i 10^i 10i称为位因子,记为digit
某位中1出现次数的计算方法:
根据当前位cur值的不同,分为以下三种情况:
1. 当 cur=0 时:此时1的出现次数只由高位high决定,计算公式为:
h
i
g
h
∗
d
i
g
i
t
high * digit
high∗digit
如下图所示,以 n=2304为例,求 digit=10(即十位)的 1出现次数。
2. 当 cur=1 时:此位1的出现次数由高位high和低位low决定,计算公式为:
h
i
g
h
∗
d
i
g
i
t
+
l
o
w
+
1
high * digit+low+1
high∗digit+low+1
如下图所示,以 n=2314 为例,求 digit = 10(即十位)的1出现次数。
3. 当 cur = 2,3,…,9时:此位1的出现次数只由高位high决定,计算公式为:
(
h
i
g
h
+
1
)
∗
d
i
g
i
t
(high+1)*digit
(high+1)∗digit
如下图所示,以 n = 2324 为例,求digit = 10(即十位)的1出现次数。
变量递推公式:
设计按照"个位、十位、…"的顺序计算,则 high / 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(log n) : 循环内的计算操作使用O(1) 时间;循环次数为数字n的位数,即 l o g 10 n log_{10}n log10n,因此循环使用O(log n)时间。
- 空间复杂度O(1) : 几个变量使用常数大小的额外空间。
2.1 案例分析
2.2 核心代码
class Solution {
public:
int countDigitOne(int n) {
if(n<0)
return 0;
long digit=1,res=0;
int high,cur,low;
high=n/10,cur=n%10;low=0;
while(high!=0 or cur!=0){
if(cur==0)
res+=high*digit;
else if(cur==1)
res+=high*digit+low+1;
else
res+=high*digit+digit;
low+=cur*digit;
cur=high%10;
high/=10;
digit*=10;
}
return res;
}
};