🔥题目
输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数。
输入:12
输出:5
解释:1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
☘️解析
这是一道比较困难的数学题。下面将用几个具体的例子,带大家弄明白代码思路。
我们不按照数字递增(1,2,3,4,5,…,1024,1025,…)的角度去考虑问题,而是从数位(个位、十位、百位、千位…)的角度去考虑问题。
我们将某一位的数字固定为1,然后去计算其他位所有可以取到的情况——想象一下,这里有一个密码箱,其中一位被固定,我们来拨动其他位的滚轮,但不能让数字小于当前数组,计数可以取到多少种情况。每个位置都固定一次1后,所有情况的总和即为最终答案。
接下来,我们把这个密码箱上数字,划分为三个区域(以数字45678为例):
1)当前位数字 cur(假设此时固定百位,即6)
2)高位数字 hi(即45)
3)低位数字 lo(即78)
并设置两个变量:
4)位数 digit(即100)
5)计数 count(全局累加)
明确了这些规定,下面我们用三个例子(一共会出现三种情况)计算一下。下面的三个例子,都假设此时计算到十位、高位为10、低位为4。
1004
十位拨到1并固定,变为1014。
高位可以取0~9,低位可以取0~9,计数加上 hi * digit
= 10 * 10 = 100
1014
十位拨到1并固定,本来就是1014。
高位可以取0~9,低位可以取0~4,计数加上 hi * digit + (lo + 1)
= 10 * 10 + 5= 105
1024
十位拨到1并固定,变为1014。
高位可以取0~10,低位可以取0~9,计数加上 (hi + 1) * digit
= 11 * 10 = 110
1034、1044、1054、1064、1074、1084、1094
同1024
根据这三个例子,很容易写出下面的代码!
🧊代码
class Solution {
public int countDigitOne(int n) {
int count = 0; // 计数
int digit = 1; // 位数
int lo = 0; // 低位数字
int hi = n; // 高位数字
int cur = 0; // 当前位数字
while (hi != 0 || cur != 0) {
cur = hi % 10;
hi /= 10;
if (cur == 0) {
count += (hi * digit);
} else if (cur == 1) {
count += (hi * digit + (lo + 1));
} else {
count += ((hi + 1) * digit);
}
lo = cur * digit + lo;
digit *= 10;
}
return count;
}
}
🌸补充
时间复杂度:O(logn) 。
解释:算法是按位进行遍历的,假如n=999,则循环了3次。有意思的是,这里的logn是以10为底数的,平常我们说的logn都是以2为底数的。