题目
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。
思路
将n分为个位和其他位
n = 4 5 6
个位
先分析个位出现1的次数:
- 个位出现1的要取决于个位之前的几位,6之前是45,所以1至少会出现45次,因为个位是6,大于1,所以个位上还会出现一次1,总共出现45 + 1 = 46 次1
再分析十位
- 因为个位从0~9经过一轮之后(总共变化10次 0,1,2…9),十位才会增加1,也就是说当十位上为1时,经过一轮变化,1会出现十次(10, 11, 12, … 19),十位上能出现几个1取决于十位之前的几位,上述例子中百位上是4,那么十位上至少会经过4轮变化,所以十位上的1会出现4次,
4 * 10 = 40
- 如果十位上原来的那个数(上述例子中是5)大于1,说明十位上的数在经过第5轮变化时(0,1,2,3,4,5)还会出现一次1,所以十位上总共出现5次1,1出现的个数为
4 * 10 + 1 * 10 = 50
- 当十位上的数等于0时,说明十位上的数只会经过四轮变化
4 * 10 = 40
- 当十位上的数等于1时,跟十位上的数大于1还有一些区别,此时需要看个位上的数值,比如416,十位数在经过第五轮变化时,只能有10, 11,12,13,14,15,16这几个数,此时1出现的个数为
4 * 10 + 6 + 1 = 47
其他位同理
当前位之前更高位的数记为times
,表示1至少出现的次数,比如456中当前位为个位时,times=45
class Solution {
public int countDigitOne(int n) {
if(n < 1) return 0;
int count = 0;
int base = 1; // base表示的是当前位的数值等于1时,1出现的次数,十位上为1,那么1可以出现10次(10, 11, ... 19)
int times = n;
while(times > 0) {
int cur = times % 10; // cur代表当前位数字的数值 123当前位为1时,cur = 3
times /= 10;
count += times * base;
if(cur == 1)
count += (n % base) + 1;
else if(cur > 1) count += base;
base *= 10;
}
return count;
}
}
时间复杂度
- O(log n) ,因为要遍历数据n的每一位上的数
空间复杂度
- O(1)