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

题目描述:

      输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例1:
输入:n = 12
输出:5

示例2:
输入:n = 13
输出:6

解题思路:

1、利用递归:
      countDigitOne(n)表示1~n整数中1出现的次数,为了找出规律,使用划分数字范围的思想来解答该题:将数字n划分为两部分,一部分是最高位high,一个是其他位的数字last。例如,对于数字1234,high为1,last为234,记pow为1000。

      所有数字对于high来说,可以分为两种情况:

(1)一种是high等于1的情况,例如,当n=1234时,可以划分为两个部分:1-999和1000-1234。对于1-999来说,其中1出现的次数为countDigitOne(pow - 1);对于1000-1234,最高位1出现的次数为235次,即last + 1,而其他位出现1的次数为countDigitOne(last)。因此对于high等于1的情况来说,countDigitOne(n) = countDigitOne(pow - 1) + last + 1 + countDigitOne(last)

(2)另一种是high不等于1的情况,例如,当n=3234时,其中high = 3,last = 234,pow = 1000,可以划分为四个部分:1-999、1000-1999、2000-2999、3000-3234。对于1-999来说,其中1出现的次数为countDigitOne(pow - 1);对于1000-1999,最高位1出现的次数为1000次,即pow,其他位出现1的次数为countDigitOne(pow - 1);对于2000-2999,最高位1出现的次数为0次,其他位出现1的次数为countDigitOne(pow - 1);对于2999-3234,最高位1出现的次数为0次,其他位出现1的次数为countDigitOne(last)。因此对于high不等于1的情况来说,countDigitOne(n) = high * countDigitOne(pow - 1) + pow + countDigitOne(last)

2、模拟密码锁:
      设数字n是个x位数,记n的第i位为ni,则n可以表示为nxnx-1…n1n0

  • 记ni为当前位,即cur
  • 将ni-1…n0记为低位,即low
  • 将nx…ni+1记为低位,即high
  • 将10i记为位因子,即digit

当前位cur中出现1的的计算方式如下:

(1)当cur = 0时,此位1的出现次数只由高位 high决定,计算公式为:high * digit,例如对于数字2305来说,high = 23,digit = 10(当前位为“10”位),“10”位出现的数字范围为0010-2219,那么固定“10”位,其他位为000-229,一共有229-0+1= 230 = 23 X10次,即high * digit。(这个计算过程有点类似于解密码锁)

(2)当cur = 1时,此位1的出现次数由高位 high和低位low决定,计算公式为:high * digit + low + 1,例如对于数字2315来说,high = 23,digit = 10(当前位为“10”位),low = 5,“10”位出现的数字范围为0010-2315,那么固定“10”位,其他位为000-235,一共有235-0+1= 236= 23 X10 + 5 + 1次,即high * digit + low + 1。

(3)当cur为其他数字时,此位1的出现次数只由高位 high决定,计算公式为:(high + 1)* digit,例如对于数字2325来说,high = 23,digit = 10(当前位为“10”位),“10”位出现的数字范围为0010-2319,那么固定“10”位,其他位为000-239,一共有239-0+1= 240= (23 + 1) X10次,即(high + 1)* digit。

因此,可以从最低位开始依次遍历数字n的所有位,然后逐步计算,即可算出表示1~n整数中1出现的次数。

以上两种解法分别参考:xujunyi大佬的题解Krahets大佬的题解

时间复杂度和空间复杂度: 第二种解法的时间复杂度为O(log10n),空间复杂度为O(1)

实现代码:

	//解法1:递归
    public int countDigitOne(int n) {
        if(n <= 0)
            return 0;
        String s = String.valueOf(n);
        int high = s.charAt(0) - '0';//最高位
        int pow = (int) Math.pow(10, s.length() - 1);
        int last = n - high * pow;
        if(high == 1)
            return countDigitOne(pow - 1) + last + 1 + countDigitOne(last);
        else
            return high * countDigitOne(pow - 1) + pow + countDigitOne(last);
    }

    //解法2:找规律
    public int countDigitOne1(int n){
        int digit = 1, res = 0;
        int high = n / 10, cur = n % 10, low = 0;
        while (high != 0 || cur != 0){
            if(cur == 0)
                res += high * digit;
            else if(cur == 1)
                res += high * digit + low + 1;
            else
                res += (high + 1) * digit;
            //更新high,low,digit,cur
            low += cur * digit;
            cur = high % 10;
            high /= 10;
            digit *= 10;
        }
        return res;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值