剑指offer-整数中1出现的次数(从1到n整数中1出现的次数)

题目:整数中1出现的次数(从1到n整数中1出现的次数)

求出1~13的整数中1出现的次数,并算出100~1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

思路分析

方法一:统计1-n之间1出现的次数,首先我们可以采用暴力解法直接数,从n到1,对于每个数都进行判断,如果含有1,则计数器加一。判断的方法是将每个数当成字符串,对于字符串的每一位是否为1进行判断即可.。

实现代码:

 1 public class Solution {
 2     public int NumberOf1Between1AndN_Solution(int n) {
 3         int count=0;
 4         while(n>0){
 5             String str=String.valueOf(n);
 6             char [] chars=str.toCharArray();
 7             for(int i=0;i<chars.length;i++){
 8                 if(chars[i]=='1')
 9                     count++;
10             }
11             n--;
12         }
13         return count;
14     }
15 }

现在来分析一下上面代码的时间复杂度,总的外循环是从n到1,循环了n次,而内部的循环与n的位数有关。所以总的循环判断次数=(9)*1+(99-10+1)*2+(999-100+1)*3+(9999-1000+1)*4+...

总的时间复杂度是比较接近O(n)线性的,但是要大于O(N)

方法二:仔细观察也是可以找到规律的。

以n=12345为例子,12345=10000+2000+300+40+5,12345可以从每一位上单独进行分析,我们以i表示位数标识,以百位为例

i=100  此时a=12345/100=123 ,  b=12345%100=45  可以拆成前后两部分的,对于两部分可以先分开计算然后最后再相乘或相加(计数原理)

  1. 如果百位上的数字大于等于2(此时为3),则前面部分共可以出现123/10+1次,即(00~12)共能出现13次1,而每个1又重复了100次(如00100~00199,01100~01199,12100~12199等, ) 故总的次数=(a/10+1)*100
  2. 如果百位上的数字等于1,如n=23145,此时a=231,b=45  此时对于前面部分共可以出现a/10+1(00~23)共24次1,但是此时只有前23个能重复100次,最后一个23100~23145只能有46次,这就与前一种情况不同,所以总的次数=a/10*100+(b+1)
  3. 如果百位上的数字等于0,如n=12045 ,此时a=120 ,b=45,此时对于前面的部分共可以出现a/10(00~11)共12次1,这12个1能重复100,后面的已经没有百位上的1出现了,所以总的次数=a/10*100

由于每一位上的情况都可以与百位上的情况相同,所以我们可以对以上规律进行总结归纳

如果百位上的数字大于等于2或是等于0,那么出现1的次数=(a+8)/10*100

(为什么加8,我们从上面可以看出大于等于2会比等于0在前面多出现一次1,为了得到对应的结果我们直接让大于等于2的向前进位后再取整,而等于0的由于其没有什么进位所以不会影响其结果)(如果题目由此变形一下,求2出现的次数,那么同理我们便可以划分为大于等于3的情况,等于2的情况,小于2的情况,此时可能就要加7)

如果百位上的数字等于1,那么总的次数由两部分组成,次数=a/10*100+(b+1)

让i从1开始逐步变为10,100,1000,。。。便可求出各个位上的1的数目,在循环的同时求和就行

代码如下:

 1 public class Solution {
 2     public int NumberOf1Between1AndN_Solution(int n) {
 3         long count=0;
 4         long i=1;
 5         for(i=1;i<=n;i*=10){
 6             long a=n/i,b=n%i;
 7             count=count+(a+8)/10*i+(a%10==1?1:0)*(b+1);
 8         }
 9        return (int)count;     
10     }
11 }

猜你喜欢

转载自www.cnblogs.com/pathjh/p/9374772.html