剑指offer面试题43 1~n整数中1出现次数

题目描述

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

设计思想一:

最容易想到的:每次对10求余,判断个数是否为1,再将原数除以10作为下次除以10求余的输入

int NumberOfBetweenAndN(unsigned int n)
{
 int number=0;

  for(unsigned int i=1;i<=n;++i)
    number+=NumberOf1(i);
   
  retrun number;
}

int NumberOF1(unsigned int n)
{
 int number=0;
  while(n)
  {
      if(n%10==1)
          number++;
      
      n=n/10;
    }
return number;
}

 法一的时间复杂度分析:对每个数字n,有O(logn)位;从1~n有n个数,所以总的时间复杂度O(nlogn),如果n很大,会很耗时。

法二:

先看最高位出现1的次数:

以21345为例,万位的的1出现在10000~19999,共10^4个,但是如果n的最高位不是1的话,比如12345,万位的1只能出现在10000~12345,共2345+1个。所以最高位是1的数,要分输入数字n的最高位是大于1,等于1,还是小于1来计算:大于1 时候,假设最高位是first,则最高位是1的数字个数NumOfFirst1=10^(n的位数-1);若first==1,则NumOfFirst1=(n-10000)+1

再分析次高位至最低位:

仍以21345为例,若是5位数,考虑低四位,在1346~11345和11346~21345这两段,每一段除万位外,1的出现次数是4*10^3=4000(选定其中1位是1,另外3位可任选,就是一个排列组合),两段就是8000

接下来考虑1~1345,可以用递归,这也是拆出1346~11345和11346~21345这两段的原因

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
       if(n<=0)
           return 0;
        
        char strN[50];
        sprintf(strN,"%d",n);
        
        return NumberOf1(strN);
    }
    
    int NumberOf1(const char* strN)
    {
        if(!strN||*strN<'0'||*strN>'9'||*strN=='\0')
            return 0;
        
        int first=*strN-'0';
        unsigned int length=(unsigned int)strlen(strN);
        
        if(length==1&&first>=1)
            return 1;
        if(length==1&&first==0)
            return 0;
        
        //当n位数不止1位时候,以21345为例,计算最高位是1 的数的个数
        int numberOfFirst=0;
        //if(first==1)
         //   numberOfFirst=atoi(strN+1)+1;
       // else if(first>1)
         //   numberOfFirst=PowerBased10(length-1);
        if(first>1)
            numberOfFirst=PowerBased10(length-1);
        else if(first==1)
            numberOfFirst=atoi(strN+1)+1;
        
        int numOfOtherDigits=first*(length-1)*PowerBased10(length-2);
        //最高位是几就分几段,每段除最高位外,剩length-1个位置可以放1,length-1位中某一位放1后
        //其余length-2每位有0~9  10种放法
        
        int NumOfRest=NumberOf1(strN+1);
        
        return numberOfFirst+numOfOtherDigits+NumOfRest;
    }
    
    int PowerBased10(unsigned int n)
    {
       int res=1;
        while(n)
        {
            res*=10;
            n--;
        }
        return res;
    }
};

关于本文用到的sprintf()的用法:https://blog.csdn.net/qq_34793133/article/details/81261178

猜你喜欢

转载自blog.csdn.net/qq_34793133/article/details/81260410
今日推荐