43 1~n整数中1出现的次数
1 题目描述
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
2 题目分析
求1-n整数中1出现的次数。这是个数学题,对于一个整数n,要找1~n所有的1,可以将某一位固定为1,然后找所有小于该数的排列组合,从最低位往最高位依次找,最终的结果就是1的总数了。
具体对于当前位有三种情况:cur = 0,cur = 1, cur > 1,还需要知道当前指向元素的数位digit(个/十/百...),以及高位大小high和低位的大小low
1. 当cur = 0时,如 1304,digit = 10,high = 13,low = 4,那么当前位为1的所有情况如下:
[00-12]1[0-9] // [00-12]表示高位所有的可能,1表示当前位,[0-9]表示低位所有的可能
故此时1的个数count = high*digit
2. 当cur = 1时,如1314, digit = 10, high = 13, low = 4,那么当前位为1的所有情况如下:
[00-12]1[0-9]和131[0-4]
故此时1的个数count = high*digit + low + 1;
3. 当cur>1时,如1324, digit = 10, high = 13, low = 4,那么当前位为1的所有情况如下:
[00-13]1[0-9]
故此时1的个数count = (high + 1) * digit
总结:
初始从最低位开始,high = n / 10, low = 0, cur = n % 10, digit = 1;
下一次各变量的状态为:high = high / 10, low = low + cur * digit, cur = high % 10, digit = digit * 10;
终止条件:high和cur都为0,即循环条件为while(high != 0 || cur != 0):
count = high * digit, cur = 0;
count = high * digit + low + 1, cur = 1;
count = (high + 1) * digit, cur > 1;
最后返回count的总和。
PS:有个更简单的规律公式,将i从1遍历到n,每次i扩大10倍:
(n/(i*10))表示(i*10)位上的个数;
min(max(n % (i*10)-i+1, 0), i)表示需要额外数的(i * 10)位上1的个数
例:1234
个位上1的数量 = 1234/10(对应1, 11, 21, ... 1221) + min(4, 1)(对应1231) = 124
十位上1的数量 = (1234/100) * 10(对应10,11,12,...110,111, 1919) + min(21, 10) (对应1210,1211,...1219) = 130
百位上1的数量 = (1234/1000)*100+min(135, 100)=200
千位上1的数量 = (1234 / 10000)*1000 + min(235, 1000)=235
3 代码
public int countDigitOne(int n) {
// 初始化
int cur = n % 10, high = n / 10, low = 0, digit = 1;
int count = 0;
while (high != 0 || cur != 0) {
if (cur == 0) count += high * digit;
else if (cur == 1) count += high * digit + low + 1;
else count += (high + 1) * digit;
low += cur * digit;
cur = high % 10;
high /= 10;
digit *= 10;
}
return count;
}