整数区间中1出现的次数

这里写图片描述

思路1

最简单的方法,分别求从1到n之间每个数中的1的个数,由于整数n的位数为O(logn),我们要判断一个数有多少个1,需要判断其每一位是否为1,这样一个数就需要判断O(logn)次,而总共有n个数需要求,那么该方法的时间复杂度为O(nlogn)。

/*
     *      累加1到n中每个整数1出现的次数。
     *      每次通过对10求余数判断当前位是不是1.
     */
    public int countNum(int n){
        int number = 0;
        for(int i = 1;i<= n;i++){
            number+=numberOf1(i);
        }
        return number;
    }
    //直接返回当前数包含的1
    public int numberOf1(int n){
        int number =0;
        while(n!=0){
            if(n %10 == 1)
                number++;
            n = n/10;
        }
        return number;
    }

思路2

按每一位来考虑,

1)此位大于1,这一位上1的个数有([n/10^(b+1]+1)*10^b
2)此位等于0,为([n/10^(b+1)])*10^b
3)此位等于1,在0的基础上加上n mod 10^b + 1

举个例子:30143
由于3>1,则个位上出现1的次数为(3014+1)*1
由于4>1,则十位上出现1的次数为(301+1)*10
由于1=1,则百位上出现1次数为(30+0)*100+(43+1)
由于0<1,则千位上出现1次数为(3+0)*1000

注:以百位为例,百位出现1为100~199*100的意思为单步出现了100~199,100次,*30是因为出现了30次100~199,+(43+1)是因为最后一次301**不完整导致。

整个过程的示意图如下:
这里写图片描述
需要注意一点:由于测试系统要求的输入数据最大为1,000,000,000,因此用int会溢出,要用long long,另外比较坑跌的一点是a可能比b大,居然都没有说明,有点坑了。

    /*
     *      根据数字规律分别统计从1到区间两端点出现1的个数 ,然后相减
     *      比如count(a,b)=count(0,b)-count(0,a-1)
     */
    public long countNum1(long num) {
        if(num<=0) return 0;
        long count  = 0;//统计1出现的总次数
        long current;//当前位的值
        long base = 10;//当前位的基
        long remain = 0;//当前位就是1时,当前位后面剩余的数,比如132,则remain=32

        while(num>0) {
            //当前位
            current = num%10;
            //统计当前位左边所有位可能的值,比如3012,当前位为2,则num=302
            num = num/10;
            //根据当前位的值确定当前位可能出现1的次数
            if(current > 1) {
                count += (num+1)*base; 
            }else if(current==1) {
                count += (num*base + remain + 1);
            }else {
                count += num*base;
            }

            //下一位的求解可能需要用到不完整的部分值
            remain += current*base;
            base *= 10;
        }
        return count;
    }

    //求指定区间的1出现的个数
    public long count(long a,long b) {
        long result;
        if(a>b) {
            result = countNum1(a) - countNum1(b-1);
        }else {
            result = countNum1(b) - countNum1(a-1);
        }
        return result;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值