《剑指offer》刷题——【时间效率】面试题43:1~n整数中1出现的次数(java实现)
一、题目描述
输入一个整数n,求1~n这n个整数的十进制表示中1出现的次数,例如,输入12,1~12这些整数中包含
1的数字有1,10,11,12,1一共出现5次
二、题目分析
方法一:不考虑时间效率
- 累加1~n中每个整数1出现的次数(每次通过对10求余数判断整数的个位数字是不是1,若此数字大于10,则除以10以后再判断个位数字是不是1)
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int number = 0;
for(int i=1;i<=n;i++){
number+=NumberOf1(i);
}
return number;
}
/**
* 一个数字包含1的个数
*/
public int NumberOf1(int n){
int number =0;
while(n!=0){
if(n % 10==1){
number++;
}
n = n/10;
}
return number;
}
}
方法二:推荐
-
若第i位(从低位向高位开始)上的数字为0,则第i位可能出现1的次数仅由更高位决定(若无最高位,则视为0),等于更高位数字当前位数的权重10^(i-1);
-
若第i位上的数字为1,则第i位上可能出现1的次数不仅受更高位影响,还受低位影响(若无最低位,则视为0),等于更高位数字当前位数的权重10^(i-1)+(低位数字+1)
-
若第i位上的数字大于1,则第i位上可能出现1的次数仅由更高位决定,等于(更高位+1)*当前位数的权重10^(i-1)
-
规律:(以下X仅指1~9)
- 1~10,个位数,任意的X都出现1次
- 1~100,十位数,任意X都出现10次
- 1~1000,百位数,任意X都出现100次
- ……
- 1~10^i,左数第二位(右数第i位),任意X都出现 10^(i-1) -
例:21345
- 个位:1~21340,1出现2134次,21341中1出现一次,故 (2134+1)*10^(i-1)=2135
- 十位:1~21300,1出现213次;21301 ~21345,4>1, 十位1出现10次,故(213+1)*10^(2-1)=2140
- 百位:1~21000,1出现21次,;21001 ~21345,3>1,百位1出现100次,故(21+1)*10^(3-1)=2200
- 千位:1~20000,1出现2次;20001 ~21345,1=1,1出现345次,故2*10^(4-1)+345=2345
- 万位:2>1,1出现次数1*10^(5-1)=10000
- 总:2135+2140+2200+2345+10000=18820
-
总结:
- 取第i 位左边的数字(高位),乘以10 ^(i−1) ,得到基础值a
- 取第i 位数字,计算修正值:
- 如果大于X,则结果为a+ 10 ^(i−1)
- 如果小于X,则结果为a
- 如果等X,则取第i 位右边(低位)数字,设为b ,最后结果为a+b+1
public class Solution {
public int NumberOf1Between1AndN_Solution(int n) {
int curr;//当前位
int low;//低位
int temp;
int high=n;//高位
int i=1;//遍历到的当前位,从最低位开始
int total=0;//总次数
while(high!=0){
high = n/(int)Math.pow(10,i);//获取第i位的高位
temp = n%(int)Math.pow(10,i);//
curr = temp/(int)Math.pow(10,i-1);//获取第i位
low = temp%(int)Math.pow(10,i-1);//获取第i位的低位
//第i位数为1
if(curr==1){
total+=high*(int)Math.pow(10,i-1)+low+1;
}
else if(curr<1){
total+=high*(int)Math.pow(10,i-1);
}
else{
total+=(high+1)*(int)Math.pow(10,i-1);
}
i++;
}
return total;
}
}