剑指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这两段的原因

#include <cmath>
#include <string.h>
class Solution {
public:
    int NumberOf1(const char* p)
    {
       if(*p<'0'||*p>'9'||!p||*p=='\0')
           return 0;
        int first=*p-'0';
       unsigned int length=static_cast<unsigned int>(strlen(p));
        if(first==0&&length==1)
            return 0;
        if(length==1&&first>0)
            return 1;
        
        int numberoffirstdigit=0;
        if(first>1)
            numberoffirstdigit=pow(10,length-1);
        else if(first==1)
            numberoffirstdigit=atoi(p+1)+1;
            
            int othernum=first*pow(10,length-2)*(length-1);
        int numRescursive=NumberOf1(p+1);
        
        return numRescursive+numberoffirstdigit+othernum;
            
    }
    int NumberOf1Between1AndN_Solution(int n)
    {
        if(n<=0)
            return 0;
        char str[50];
        sprintf(str,"%d",n);
        return NumberOf1(str);
    }
};

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值