【剑指Offer】31、从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出现的次数,而对于每个整数,可以通过对10求余判断个位数字是不是1,然后除10再判断下一位。这种思路很简单,但是对于数字n,有O(logn)位,n个数字依次判断的时间复杂度为O(nlogn),计算量比较大。

  这里我们采用一种数学方法,对数n的每一位单独讨论,记每一位的值为weight。

  首先,我们考虑个位,个位每隔10个数就会出现一个1,如1,11,21等等,以10为分隔阶梯,我们可以知道每次从1到10 的一轮中,会出现一个1,出现了几轮取决于它前面的数,最后根据个位上的数会出现一个不完整的一轮,若为0,这说明没有出现1,否则出现了1。所以个位上总共出现的1的个数为:n/10 + (n%10==0?0:1)。

  然后考虑十位,十位与个位的不同之处在于:从1到n,每增加10,十位的weight才会增加1,所以,一轮0-9周期内,1会出现10次,也就是从10到19这十个数。这样的数每隔100次才会出现一次,出现了几轮取决于它前面的数。同样最后会有一个不完整的一轮,这取决于十位上的数字,如果十位数weigth大于1,说明最后一轮的完整十次都出现了;weight为0,说明最后一轮没有出现1;如果正好为1,那么取决于个位数字是几。

  进而再考虑更高位,分析方法和十位类似。

  总结起来,我们可以得到以下规律:

   记当前位值为weight,它前面的数为round(代表了循环轮次),后面的数为former(初始为0,代表最后的一个不完整轮次),当前位权为base。

  • 若weight为0,则1出现次数为round*base
  • 若weight为1,则1出现次数为round*base+former+1
  • 若weight大于1,则1出现次数为rount*base+base

  举例:

  534 = (个位1出现次数)+(十位1出现次数)+(百位1出现次数)=(53*1+1)+(5*10+10)+(0*100+100)= 214

  504 = (50*1+1)+(5*10)+(0*100+100) = 201

  514 = (51*1+1)+(5*10+4+1)+(0*100+100) = 207

  编程实现(Java):

   public int NumberOf1Between1AndN_Solution(int n) {
        int count=0;
        int weight=0;  //位值
        int base=1; //权重
        int former=0;
        int round=n;
        while(round!=0){
            weight=round%10;
            round=round/10;
            former = n%base;
            if(weight>1)
                count += (round*base+base);
            else if(weight==1)
                count += (round*base+former+1);
            else
                count += (round*base);
            base *= 10;
        }
        return count;
    }

  本文参考: CSDN博客yi_afly,特此说明

posted @ 2019-05-06 21:57  gzshan  阅读(1246)  评论(0编辑  收藏  举报