剑指offer 计算1到n中所有1出现的次数

    

      如题,显而易见,我们可以依次计算所有数中的1来完成该功能:

int NumOfOneBetweenN(int n)
{
    int sum = 0;
    for(int i=1;i<n;++i)
        sum += NumOfOne(i);
    return sum;    
}

int NumOfOne(int i)
{
    int sumi = 0;
    while(i)
    {
        if(i%10 == 1)
            sumi++;
        i/=10;    
    }
    return sumi;
}

      时间复杂度为O(n*logn),效率不高。

      那么,换一种思路考虑,计算每个位出现1的次数,当然,要先找到数学规律,这需要归纳推理:

      设输入数字为n,1出现的次数为t,每一位出现的次数为ti.

      n为个位数: 显而易见,  n>=1,t=1;

                  n=0,t=0

                     n为十位数: n=13,个位出现1的情况:1,11, 十位出现1的情况:10,11,12,13       t=2+4=6;

                                       n=23,个位出现1的情况:1,11,21, 十位出现1的情况:10~19        t=3+10=13;

              n=33,个位出现1的情况:1,11,21,31, 十位出现1的情况: 10~19  t=4+10=14;

            我们发现,当十位数>1,十位出现的次数固定: t10=10;

               十位数=1,次数由个位大小决定: t10=t1+1;

               个位数>1,次数由十位大小决定: t1=t10+1.

       n为三位数: n=123,个位出现1的情况:1~91,101~121, 十位数出现1的情况: 10~19,110~119 , 百位出现1的情况: 100~123   t=13+20+24=57;

            n=223,个位出现1的情况:1~91,101~191,201~221, 十位出现1的情况: 10~19,110~119,210~219, 百位出现1的情况: 100~199 t=23+30+100=153;

            我们发现,十位的次数被高位影响,个位的次数也被高位影响,而百位和上面十位>1的情况一样,次数固定,=1的情况又会被低位影响

            不妨,我们可以设一个五位数,我们要计算百位出现1的次数,那么可以把数字分成三部分,百位之前的高位,百位,百位之后的低位.

            1.百位=0,百位出现1的次数只与高位有关.比如12013,百位出现1的情况:00100~00199,01100~01199...11100~11199.共1200个,也就是高位*本位固定后的次数,即12*100;

            2.百位>1,百位出现1的次数只与高位有关.比如12313,百位出现1的情况,00100~00199,01100~01199...12100~12199,共1300个,也就是(高位+1)*1本位固定后的次数,即13*100;

            3.百位=1,百位出现1的次数与高低位都有关.比如12113,百位出现1的情况,00100~00199...11100~11100,还有12100~12113,共1314个,也就是高位*本位固定后的次数+低位+1,即12*100+114

            归纳: 某一位=0的时候,该位出现1的次数被高位决定,即 0该位~(高位-1)该位,共高位*该位固定数;

              某一位>1的时候,该位出现1的次数被高位决定,即 0该位~高位该位,共(高位+1)*该位固定数;

             某一位=1的时候,该位出现1的次数被高低位决定,即 0该位~(高位-1)该位 , 该位0~该位低位,共高位*该位固定数+低位+1;

           本质上,其实只有两种情况: 1.ti=0||ti>1,可以被高位按固定次数列举完

                         2.ti=1,被高位按固定次数列举完之后,还有不足固定次数的,由低位决定

int NumOfOneBetweenN(int n)
{
    int count = 0;
    int i = 1;
    int cur = 0,after = 0,before = 0;
    
    //循环处理每一位
    while((n/i)!=0)
    {
        cur = (n/i)%10;
        before = n/(i*10);
        after = n - (n/i)*i;
        
        if(cur>1)
            count += (before+1)*i;
        else if(cur==0)
            count += before*i;
        else
            count += before*i + after + 1;
            
        i*=10;                  
    }
    return count;
}

  

          

              

              

原文地址:https://www.cnblogs.com/Duikerdd/p/12179314.html