剑指43—1~n中1出现的个数

剑指43—1~n中1出现的个数

题目

对给定的n,统计1~n中每一位上1出现的次数,最后输出总和。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

示例

输入:12

输出:5

思路

我们可以从低到高,以每一位为单位,去找到该位上1出现次数的规律,来统计次数。

以210为例:

首先是个位,210个位是0,个位出现1的个数是(被掩埋掉的1):高位的数据量,总计 21 个。
没有显式的1,但是该数字具有高位(百位,十位),这些都是通过个位进位来的,即高位每增加1,那么个位会循环往复 从 0 到 1 再到 9 最后到 0 进位,那么高位有多少个数,个位就出现过多少个 1(被掩埋掉了),这里高位是 21 所以个位出现过 21 个 1;

再进到十位,十位上是1,具有显式的1,所以十位上出现1的个数是(被掩埋掉的1 + 显式出现的1): 高位的数据量×10 + 低位的个数 + 1,总计 21 个。
因为十位是 100 才会向高位进1,因此十位的高位每增加1,十位上的1就会出现十次,这里十位的高位是2,因此十位上被掩盖的1就出现了2*10=20次,而十位上具有显式的1,则它出现次数和个位有关,个位是0,则十位显式出现 一次 1,如果个位是5,则十位显式出现六次 1;

再接着是百位,百位上是2,则百位上出现1的个数是(被掩盖的1):(高位的数据量+1)× 100,总计 100 个。
首先百位进位要1000个数,其中1的个数是100个,百位上的数比1大,表示百位上的1已经全部被掩盖,但是还没有达到进位的程度,因此 高位已有的数据量还要加上1,再乘以百位的权重 100 ,

因此 210 总计出现 142 个1。

从中推出一般规律:

我们从低位开始向上遍历:
用 cur 代表遍历到的第 i 位上的数字,
用 low 代表低于该位总计有多少数字,比如 210 中遍历到十位,则 low = 0;
用 high 代表高于该位总计有多少数字,比如 210 中遍历到十位,则 high = 2;
用 digit 代表该位的权重,digit = 10 ^ i,十位的权重自然是 10;

cur只有三种情况:
cur = 0,代表刚刚完成进位,该位贡献的 1 只有隐式进位的 1,其个数等于 high × digit;
cur = 1,该位贡献的1有隐式进位的 1 和显式的 1(记得加上低位全是0的情况) ,其个数等于 high × digit + low + 1
cur = 2~9,该位贡献的1全部都是隐式进位的1(记得加上没有达成进位但是被掩埋的情况),其个数等于 ( high + 1 ) × digit ;

因此在遍历时维护好 cur 、 low 、high 和 digit 即可。

代码

public int countDigitOne(int n) {
//        初值
        int digit = 1, res = 0;
//        cur从个位开始,逐个对high取余,初值从n中取得
        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;
//            更新
//            low是低于cur的所有数字的数量统计,那么就是cur*digit的累加和
            low += cur * digit;
//            cur要进位,所以要对high取余,获取更高一级的位上的数字
            cur = high % 10;
//            high进位,持续自除
            high /= 10;
//            进制递增
            digit *= 10;
        }
        return res;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值