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

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

在这里插入图片描述
首先明确一点,我们的算法是给定 n,我们将 1~n 的所有数的个位出现 1 的次数 + 1~n 的所有数的十位出现 1 的次数 + 1~n 的所有数的百位出现 1 的次数 + …,就是最终的答案。我们通过找规律的方法,只需要知道 n,就能算出 1~n 的每一位上一共能出现多少个 1。请看下面的步骤:

我们首先来思考 n 的某一位可能是哪些数字,因为这会影响到我们找规律的公式。不难想到,n 的某一位要么是 0,要么是 1,要么是 2/3/4/…/9。
我们现在以十位为例子来推导我们的公式。我们设立一个变量 digit,用来表示当前的数位(digit 等于几当前的数位就是几位数)。所以如果是十位,digit = 10。
1、假设 n = 1204。此时十位是 0,我们把十位左边的叫做高位 high,所以这里 high = 12;把十位右边的叫做低位 low,所以这里 low = 4。十位能出现 1 的数字范围是:0010~1119,12xx没法使十位为1,高位的范围为00~11,低位的范围为0 ~ 9,因此十位出现1的次数为1210 = high * digit。
2、假设 n = 1214。此时十位是 1,同样我们有 high = 12,low = 4。十位能出现 1 的数字范围是:0010~1214,高位的范围为00~12,低位的范围为0 ~ 9(当高位为12时,地位只能为0 ~ 4),因此十位能出现1的次数为12 * 10 + 5 = high * digit + low + 1。
3、假设 n = 1234。此时十位是 3,同样我们有 high = 12,low = 4。十位能出现 1 的数字范围是:0010~1219,高位的范围为00 ~ 12,低位的范围为 0 ~ 9,因此十位能出现1的次数为13 * 10 = (high + 1)
digit。

现在我们知道了如果找到 n 的某一位一共能出现 1 的次数。那我们如何循环地把每一位都累加起来呢?也就是代码逻辑怎么写呢?请看下面的步骤

我们的思路是从个位开始计算起,所以我们首先让 digit = 1(表示个位);high = n / 10(表示个位左边的全部);low = 0(表示个位右边的全部,也就是没有);cur = n % 10(表示个位上的数)。
然后循环来移动 cur,确保 n 的每一位都被计算过。每次循环都用 cur 当前的值来匹配上述的三个公式,从而计算该位 1 一共能出现的次数。
注意我们循环退出的条件是 high 和 cur 都等于 0。因为都等于 0 就意味着 n 已经彻底遍历完了。不然的话:
假如退出的条件只有 high 等于 0:那假如 n = 1024,然后此时 cur 在千位,则 cur = 1,high = 0,low = 024。如果此时退出循环,我们还没来得及计算千位能出现的 1 的次数。
假如退出的条件只有 cur 等于 0:那假如 n = 1024,然后此时 cur 在百位,则 cur = 0,high = 1,low = 24。如果此时退出循环,我们还没来得及计算百位和千位能出现的 1 的次数。

class Solution {
    public int countDigitOne(int n) {
       int digital = 1, cur = n % 10, high = n / 10, low = 0;
       int sum = 0;
       while(high != 0 || cur != 0) {
           if(cur == 0) sum += high * digital;
           else if(cur == 1) sum += high * digital + low + 1;
           else sum += (high + 1) * digital;
           low += cur * digital;
           cur = high % 10;
           high = high / 10;
           digital *= 10;
       }
       return sum;
    }
}

时间复杂度 O(logn):循环次数为n的位数log10(n)。
空间复杂度 O(1)
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值