[剑指 Offer 43. 1~n 整数中 1 出现的次数]

剑指 Offer 43. 1~n 整数中 1 出现的次数

输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。

例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

1、分析:

我们可以直接从题意出发,十进制位出现1的次数,也就是说,答案次数就是遍历每个数位统计1出现的次数,再累加

例如12,分别有两个个位1和三个十位1;我们可以对比10,分别有一个个位1和一个十位1;以及11,分别有两个个位1和两个十位1;

为了方便位数划分,我们划分为 1到9是一个层(个位层),1到11很明显是1到9,再多出10和11; 12就再多一个12.

那么第一层 0到 9 可以写成 00 到 09 ,想知道个位的1出现次数,自然是看个位数与1的关系,到底是大于等于还是小于

然后第二层 可以写成 10 到 99 ,需要分别查看统计个位和十位。

想知道个位的1出现次数,必需分成两部分。例如 90几(设为x)必需分为 90 和 个位数,

  • 90存在的个位1数明显就是90/10 = 9 : [1,21,31……,81]

  • 然后是个位数的值先拿出来 x%10,很明显分为三种情况,与1的关系,到底是大于等于还是小于

    1、例如余数为0 (这个位是0),那就没1了,不用加

    2、例如余数为1(这里要看低位),低位个数

    3、例如余数大于1,那就加1,实际上这个位数

然后想知道十位的1出现次数,也分成两部分,x/10x%10

  • 90存在的十位1数明显就是90/100 = 0 , 也就是说,010,012这些数,百位高位没有复用,例如 110,210,212这种。

  • 然后是十位数的值先拿出来 x%10,很明显也分为三种情况,与1的关系,到底是大于等于还是小于

    1、例如十位是0 ,那就没1了,不用加(加0),例如 【02,03】

    2、例如十位为1(这里要看低位),例如【10,11,12,13】,十位上的1,全看这个余数往前数位数的个数

    注意这里 12 % 100 = 12, 那么就是 12 - 10+ 1 = 3。 【经典长度公式

    3、例如十位大于1,那么就是说,该位所有1都跑过了,加当前位数个数。例如,100以内十位1,只有10到19。

2、总结

很明显我们需要遍历各个位置上的数位,并进行统计。统计主要是看高位和低位。(假设低位包含当前统计位)

  • 高位复用当前位的1 ( 数字 / ( 10 X 位数 ) * 位数) 低位数字%( 10 X 位数 )

  • 当前位存在与1关系的三种情况 【 0, 数当前位为1的个数(长度公式),当前位数个数】

    自己举例继续分析,当前位数为0,那么数不出来,长度公式值必然小于等于0,所以前两个用max合并。

    当前位数为大于1的,再怎么数,数量你也超不过当前位数。例如 10到19一共就10个,数91也还是10个十位1。

  • 所以公式为

    num = ( x / ( 10 * digital ) * digital) + min( max( x % ( 10 * digital ) - digital + 1, 0), digital)

    x % ( 10 * digital ) - digital + 1就是搁那数数

public int countDigitOne(int n) {
    int res = 0, digital = 1;
    while (digital <= n) {//从个位到数字n所在的最高位
        res +=  ( n / ( 10 * digital ) * digital)  +
                Math.min( Math.max( n % ( 10 * digital ) - digital + 1, 0), digital);
        digital *= 10;
    }
    return res;
}

如果不会化简,就用ifelse判断

public int countDigitOne(int n) {
    int res = 0, digital = 1;
    int high = 0, low = 0, cur = 0;
    while (digital <= n) {//从个位到数字n所在的最高位
        high =  ( n / ( 10 * digital ) * digital);
        //判断当前位
        cur = n % ( 10 * digital ) / (digital);
        if (cur == 0) low = 0;
        else if (cur == 1) low = n % ( 10 * digital ) - digital + 1;
        else low = digital;
        res += (high + low);
        digital *= 10;
    }
    return res;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值