1、题目来源:
最近在刷题,《剑指offer》当然是不二之选,但是书中偶尔有些解法并不完美。比如第32题,书中给出的解法过于繁复,想在面试极短的时间内完整写出,难度较大。上网搜索了一下其他解法,讲解的都不太容易理解,故写了此篇博客,记录一下自己的理解。
2、题目描述:
输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次。
3、具体解法:
暴力的解法一个一个数字考虑,时间复杂度过大,肯定是打动不了面试官,拿下sp的。我们从位的角度来考虑,以三位数534为例。
情况一:个位上出现1
此时将个位固定为1,base记为1。那么考虑百位的取值为0、1、2、3、4五个数,十位可取0-9十个数,一共有5*10=50种组合。再考虑百位取5,此时因为十位有限制,最大不过3,所以有3+1种组合(501,511,521,531),故总共有count = 53 +1 = 54种(即count = round*base + base)组合。
如果个位(即weight)等于1,n = 531,情况同上,count = 53 + 0 + 1 = round*base + 1 + former;(此时former = 0)
如果个位等于0,n = 530,则531取不到,此时count = 53 = round*base;
情况二:十位上出现1
此时将十位固定为1,base = base * 10 = 10。
如果weight > 1,如weight = 3,n =534,此时考虑百位的取值为0、1、2、3、4、5六个数,个位可取0-9十个数,一共有count = 6*10=round*base + base = 60种组合。
如果weight =1,n = 514,此时当百位为5时,个位就不能取0-9所有数了,如取5,则515就超过514了。此时考虑former的值,个位有0,1,2,3,4五种取法。故count = 5*10 + 4 + 1 = round * base + former + 1;
如果weight = 0,n = 504,此时当百位为5时,十位取不到1,故count = round*base = 5 * 10 = 50;
总结:
对其它位来说,记每一位的权值为base,位值为weight,该位之前的数是former,则
- 若weight为0,则1出现次数为round*base
- 若weight为1,则1出现次数为 round * base + former + 1
- 若weight大于1,则1出现次数为round*base + base
4、代码(java)
class Solution {
public int countDigitOne(int n) {
if (n < 1){
return 0;
}
int count = 0;
int base = 1;
int round = n;
while(round > 0){
int weight = round % 10;
round = round / 10;
count = count + round * base;
if (weight == 1){
count = count + 1 + n%base;
}else if (weight > 1){
count = count + base;
}
base = base * 10;
}
return count;
}
}
5、运行结果