任意非负整数,求1~n之间含有1的整数的个数

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

解析:

1、将整数划分成2部分,以个位、十位、百位.....分别为分界线,以32459为例,以百位为分界位为例,可分为324和59两部分
            int a = n/i,b = n%i;  i = 100; //a为前一部分,b为后一部分
2、当分界位为0时,32059 左部分有(0~31)32种可能时,右边有10*10种可能,共有32*10*10种选择,
      因为百位为1时,小于32059的数左边只能取(0~31),当左边为32时,右边部分不管取什么,百位都不能为1
            if(a%10 == 0) {sumValue = sumValue + a/10*i;}
3、当分界位为1时,32159 左部分有(0~31)32种可能时,右边有10*10种可能,共有32*10*10种选择
      左边为32时,右边部分有(0~59)60种选择
            if(a%10 == 1) {sumValue = sumValue + (a)/10*i + b + 1;}
4、当分界位大于1时,32259百位可以取到1,左部分有(0~32)33种可能时,右边有10*10种可能,共有33*10*10种选择
      为什么这个是33,以上两者是32,因为不管左部分取(0~31)还是32时,右边都有10*10种可能。
            if(a%10 >= 2) {sumValue = sumValue + (a/10 + 1)*i;}   
      这三个条件语句可以融合成一条语句即:

代码为:

  int NumberOf1Between1AndN_Solution(int n)
    {
        if(n < 0) return 0;
        int sumValue = 0;
        for(int i = 1; i <= n;i *= 10)
        {      
          int a = n/i,b = n%i;
          sumValue = sumValue + (a + 8)/10*i + (a%10 == 1)*(b + 1);           
        }       
    return sumValue;
    }
为什么是(a+8)/10,这一句是以上三种条件下的融合,

根据以上三式可知,当分界位为0和1时,0和1加8除以10还是0,2~9加8除以10为1,(a/10 + 1)的效果,左边多一种可能。

还有一种方法,就是从1到n每个数都进行判断是否含有1,它的时间复杂度为O(n*logn),上种方法时间复杂度为O(logn),当n特别大时,快慢可想而知,但是这个方法容易想到,上一个方法难以想到。

 int NumberOf1Between1AndN_Solution(int n)
    {
        if(n < 0) return 0;
        int sumValue = 0;       
       for(int i = 1; i <= n; ++i)
           {
            sumValue += NumberOf1(i);
           } 
    return sumValue;
    }

 int NumberOf1(int num){
        int sum = 0;
        while(num){
            if(num%10 == 1){sum++;}          
            num = num/10;
        }
       return sum;    
        
    }


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值