剑指offer之1的个数

题目描述

给定一个十进制的正整数N,从1开始,到N的所有整数,然后数一下其中1出现的个数。

思路

当然,这个题从1列举到N可以求出来,但是时间复杂度达到O(n*lgn),同时也可以将这个看做一个数学问题。
先看看1位数的情况
如果N=4,那么从1到4所有的数字,1、2、3、4,只有个位上才有可能出现1,而且只是出现一次,所有当n>1时,f(n)=1。
2位数的情况
如果N=14,那么从1到14所有的数字,会发现个位和十位都有可能出现1,分开考虑,个位出现1的有1和11,十位出现1的有10、11、12、13、 14,所有f(n)=2+5=7。在比如24的情况,个位出现1只有1和11和21,十位出现1得有10到19,所有f(n)=3+10=13。
得出结论:如果N的个位数大于等于1,那么个位出现1的次数等于十位上的数+1(比如24个位出现3个1,那么等于十位2+1);如果个位为0的话,个位出现1的次数等于10位1出现的次数;如果十位上出现1,那么十位上1出现的次数等于个位上的数+1,(比如14,十位上出现了5个1,等于个位上得数4+1),如果个位数的数大于1,那么十位上出现1的次数为10。


f(4)=个位数的1+十位上的1 = 1
f(14)=个位的1+十位的1 = (个位大于1,个位出现的次数为十位+1为1+1=2)+(十位为1,那么十位上1出现的次数为个位数+1=4+1=5)=7
f(24)=个位数1+十位数1 = (个数大于1,类比,个位有3个1)+(十位数大于1,那么十位上出现1的次数为10) = 13
f(94)=个位出现的1+十位出现的1=(个位大于1,则出现1的个位为十位上的数+1=10)+(十位数大于1,那么出现1的个数为10)=20


推理到一般
若百位上的数字为0,那么百位数1出现的次数是由更高位决定的,比如12023,百位数出现1的次数是100~199、1100~1199、2100~2199…11100~11199一共是12*100=1200个
若百位数的数字为1,比如12113,那么除了高位的影响外还有低位的影响,高位是1200个,低位会出现12100…12113,等于(113)+1个。
若百位大于1,比如12213,则百位出现1的个数仅仅由更高位决定,(100~199、1100~1199、2100~2199、11100~11199、12100~12199)一共1300个,为(12+1)*100个。

public static int oneNumber(int n){
        int icount = 0;
        int ifactor = 1;
        int iLowerNum = 0;
        int iCurrNum = 0;
        int iHigherNum = 0;

        while(n/ifactor != 0){
            iLowerNum = n - (n/ifactor)*ifactor;
            iCurrNum = (n/ifactor)%10;
            iHigherNum = n/(ifactor*10);

            switch (iCurrNum) {
            case 0:
                icount += iHigherNum *ifactor;
                break;
            case 1:
                icount += iHigherNum *ifactor +iLowerNum +1;
                break;
            default:
                icount += (iHigherNum +1)*ifactor;
                break;
            }
            ifactor *= 10;
        }
        return icount;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值