题目描述
求出1 ~ 13的整数中1出现的次数,并算出100~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。
解题思路
法一:依次遍历每个数,判断每个数里面是否包含1
法二:归纳法
具体讲方法二:
设定1、10、100、1000…为中间点,分别考虑个位、十位、百位上…有多少个包含1的数进行分析,目的为找出规律
根据设定的位置i(i=1、10、100…)对n进行分割,可以分为两部分,高位为a=n/i,低位为b=n%i。
举个例子i=100时:
1.若百位对应的数字大于等于2,设n=71343,那么a=713,b=43。此时百位为1的次数有a/10+1=72次。72次中每次都包含0到99这100个连续的数,那么百位为1的数一共有72×100=7200次。也即 (a/10+1)×100。
2.若百位对应的数为1,设n=13198,那么a=131,b=98。此时百位对应的数就是1,需要分两部分考虑:
(1)对于a/10=13次百位为1(0到12),每次都包含包含0到99个连续数,那么百位为1的数一共有13×100=1300次。也即a/10×100。
(2)对于最高两位为13时,百位为1,此时百位为1所包含的数共有0到98个,即b+1个。
综上,百位对应的数为1时,百位为1的数共有a/10×100+b+1个。
3.若百位对应的数为0,设n=13066,那么a=130,b=66。那么百位为1的次数为0到12,13次,即a/10,每次均包含0到99个连续数,那么满足要求的数共有12×100=1200个。也即a/10×100。
规律如上,每一位都可以分为上述三种情况!!!!!
当百位对应的数为0或者大于等于2时,有(a+8)/10次包含0到99个连续数。之所以补8,是因为当百位为0,则a/10==(a+8)/10,当百位>=2,补8会产生进位位,效果等同于(a/10+1)。
当百位对应的数是1时,需要增加未满100的数b+1个。
代码实现
去博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片
.
public class numberOf1Between1AndN {
//求出1~n中所有数字出现1的次数
public int numberOf1_1(int n){
int number=0;
for (int i=1;i<=n;i++){
number+=numberof1(i);
}
return number;
}
public int numberOf1(int n){
int number=0;
for (int i=1;i<=n;i*=10){
int a=n/i,b=n%i;
number=number+(a+8)/10*i+(a%10==1?1:0)*(b+1);
}
return number;
}
public static void main(String[] args) {
numberOf1Between1AndN test=new numberOf1Between1AndN();
int count=test.numberOf1_1(13);
System.out.println(count);
}
}
class Solution {//这种更好理解
public int countDigitOne(int n) {
int dight=1,number=0;//位因子dight
int high=n/10,cur=n%10,low=0;//分别为高位、当前位、低位
while (high!=0||cur!=0){
if (cur==0){
number+=high*dight;
}else if (cur==1){
number+=high*dight+low+1;
}else {
number+=(high+1)*dight;
}
low+=cur*dight;
cur=high%10;
high/=10;
dight*=10;
}
return number;
}
}
总结
本题来源于面试经典教材《剑指offer》中 归属于其他类型题目。
同许多在算法道路上不断前行的人一样,不断练习,修炼自己!
如有博客中存在的疑问或者建议,可以在下方留言一起交流,感谢各位!
最后,感谢AIAS!
觉得本博客有用的客官,可以给个赞鼓励下! 嘿嘿