剑指 Offer 43. 1~n 整数中 1 出现的次数
思路:枚举每一数位上 1 的个数
公式:
比如:计算321
统计10位上出现的1的次数
1.对于从0开始的每100个数,十位上的1都会出现10次,321拥有3个这样的循环,每个循环 十位 上都出现了10次1,一共出现了30次1,这部分为⌊ 321/100 ⌋×10
2.对于不完整的循环,存在三种情况,
- 305:十位为0,不会出现1,
- 315:十位为1,不完全出现1,会出现15 - 10 + 1个1
- 325:则会出现全部的1
计算时可以用取余数的方式拿到除去高位剩下的数,与10比较,计算与10的差值,由于是从10开始计算,需要+1,最后根据结果的正负,如果是负数,代表没有1,取0,如果是正数,可能会存在超过10的情况,所以我们取最多不超过10.
min(max(n −100+1,0),100)
4.最后将两部分加起来即可
/*
剑指 Offer 43. 1~n 整数中 1 出现的次数
*/
public class SolutionJZ43 {
public int countDigitOne(int n) {
// 从第一位开始计算
long mulk = 1;
// 总数为0
int ans = 0;
while (n >= mulk) {
// 拿到当前数位个数
long a = (n / (mulk * 10));
// 计算当前数位正多少 比如 87655 选择千位,就是87
long b = a * mulk;
// 计算当前位置下的余数 比如 87655 选择千位,就是655
long c = n % (mulk * 10);
// 当 n < mulk 时,「mulk位」上不会出现 1
// 当 mulk < n < mulk * 2 时,「mulk位」上出现 1 的范围为 [mulk, n] 所以出现了 n - 100 + 1次1
// 当 ≥ mulk * 2 时,「mulk位」上出现了全部 1。
long d = Math.max(c - mulk + 1, 0);
// n < 100 答案取0 ,n >= 100 , 答案选当前位数
long e = Math.min(d, mulk);
ans += b + e;
mulk *= 10;
}
return ans;
}
}