《剑指offer》-[第5章:优化时间与空间效率-5.1:时间效率]-题32:从1到n的整数中1出现的次数

本文介绍了一种高效算法,用于计算1到任意整数N之间十进制表示中数字1出现的总次数。通过分析数字构成的规律,提出了一种基于位运算的方法,避免了对每个数字进行单独处理的低效方式。该算法的时间复杂度仅为O(log10n),实现了快速准确的计算。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、问题描述

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

2、解题思路

  • 边界条件:(1)不合法输入,输入整数为0或负;

  • 思路1:对于1到n的每个整数,分别计算其十进制表示中1出现的次数,最后将这n个数的计算结果累加起来,就得到了1总共出现的次数。这个方法的关键是如何统计一个整数的十进制中1出现的次数:可以利用求一个数的十进制表示中各数位上的数字算法来解决这个问题,即统计该整数各个数位中1的个数。

  • 思路2:这道可以从数字出发找规律。其实在《编程之美》中就总结了相应的规律:

  • (1)1的数量:

  • 如果第iii(从右向左编号,编号从1开始)位的数字为0,那么第iii位可能出现1的次数times(i)times(i)times(i)由更高位决定(若无最高位,视最高位为0),且times(i)times(i)times(i)=高位所有数字 * 当前位的权重10i−110^{i-1}10i1。例如:输入数字为4039,那么百位数字出现1的数字有100-199,1100-1199,2100-2199,3100-3199。百位出现1的次数times(3)=4∗102=400times(3)=4*10^{2}=400times(3)=4102=400

  • 如果第iii位的数字为1,那么第iii位数可能出现1的次数times(i)times(i)times(i)不仅由更高位决定,也受更低位的影响(若无最高位,视最高位为0),即times(i)times(i)times(i)=高位数字 * 10i−110^{i-1}10i1 + 低位数 + 1;

  • 如果第iii位的数字大于1(2-9),那么第iii位数可能出现1的次数times(i)times(i)times(i)由更高位决定,同时还要加上第i位为1的情况,即times(i)times(i)times(i)=高位数字 * 10i−1+10i−110^{i-1} + 10^{i-1}10i1+10i1

  • (2)X的数目:

  • 从1到10的这10个数,在它们的个位中,任意的X都出现了1次;

  • 从1到100的这100个数,在它们的十位数中,任意的X都出现了10次;

  • 从1到1000的这1000个数,在它们的百位数中,任意的X都出现了100次;

  • 从1到10i10^{i}10i的这10i10^{i}10i个数,在它们的第iii位数中,任意的X都出现了10i−110^{i-1}10i1次。

  • 下面以n=435,X=3为例,计算1-n这n个数X出现的次数:

  • 首先是个位(第1位),其高位=43,低位=0,由于个位数字5>X,所以个位数出现X的次数由高位决定,最后再加上个位等于X的情况,即出现次数=43∗100+100=43+1=4443*10^{0}+10^{0}=43 + 1=4443100+100=43+1=44

  • 其次是十位(第2位),其高位=4,低位=5,由于十位数字3=X,所以十位数出现X的的次数不仅由最高位决定,也由最低位决定,即出现次数=4∗101+5+1=464*10^{1}+ 5+1=464101+5+1=46

  • 最后是百位数(第三位),其高位=0,低位=25,由于百位数4 > X,出现X的的次数由最高位决定,然后加上百位等于X的情况,即出现次数=0∗102+102=1000*10^{2} + 10^{2}=1000102+102=100

  • 总结一下以上的算法,可以看到,当计算右数第 iii 位包含的 XXX 的个数时:

  • 取第iii位左边(高位)的数字,乘以 10i−110^{i-1}10i1 ,得到基础值 aaa

  • 取第 iii 位数字,计算修正值:

  • 如果大于 XXX,则结果为 a+10i−1a+ 10^{i−1}a+10i1

  • 如果小于 XXX,则结果为 aaa

  • 如果等于 XXX,则取第 iii 位右边(低位)数字,设为 bbb ,最后结果为 a+b+1a+b+1a+b+1

  • 相应的代码非常简单,效率也非常高,时间复杂度只有 O(log10n)O( log 10 n)O(log10n)

3、代码实现

public class Problem_32 {
    public int NumberOf1Between1AndN_Solution(int n) {
        if (n <= 0){
            return 0;
        }

        int count = 0;

        String n_str = String.valueOf(n);

        for(int i=1;i<n_str.length() + 1;i++){

            int high = n/(int)Math.pow(10,i);

            int temp = n % (int)Math.pow(10,i);
            int  low = temp % (int)Math.pow(10,i-1);
            int current = temp / (int)Math.pow(10,i-1);
            int base = high * (int)Math.pow(10,i-1);
            if (current < 1){
                count += base;
            }
            else if (current == 1){
                count += base + low + 1;
            }
            else {
                count += base +(int) Math.pow(10,i-1);
            }

        }

        return count;

    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Albert_YuHan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值