剑指Offer-整数中1出现的次数(从1到n整数中1出现的次数)

题目描述

求出1~ 13的整数中1出现的次数,并算出100~ 1300的整数中1出现的次数?为此他特别数了一下1~13中包含1的数字有1、10、11、12、13因此共出现6次,但是对于后面问题他就没辙了。ACMer希望你们帮帮他,并把问题更加普遍化,可以很快的求出任意非负整数区间中1出现的次数(从1 到 n 中1出现的次数)。

有一种把数字拆成数组,再循环判断数组中有没有1的思路,个人不推荐,复杂度太高。下面两种都是基于规律得到的。

解题思路—基础版:(在牛客网上看到这个大佬的解释特别清晰,在此借鉴!)

设N = abcde ,其中abcde分别为十进制中各位上的数字。
如果要计算百位上1出现的次数,它要受到3方面的影响:百位上的数字,百位以下(低位)的数字,百位以上(高位)的数字
① 如果百位上数字为0,百位上可能出现1的次数由更高位决定。比如:12013,则可以知道百位出现1的情况可能是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,… 11100~11199,一共1200个。可以看出是由更高位数字(12)决定,并且等于更高位数字(12)乘以当前位数(100)。
② 如果百位上数字为1,百位上可能出现1的次数不仅受更高位影响还受低位影响。比如:12113,则可以知道百位受高位影响出现的情况是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,…,11100~11199,一共1200个。和上面情况一样,并且等于更高位数字(12)乘以当前位数(100)。但同时它还受低位影响,百位出现1的情况是:12100 ~12113,一共114个,等于低位数字(113)+1。
③ 如果百位上数字大于1(2 ~ 9),则百位上出现1的情况仅由更高位决定,比如12213,则百位出现1的情况是:100 ~ 199,1100 ~ 1199,2100 ~ 2199,…,11100 ~ 11199, 12100~12199,一共有1300个,并且等于更高位数字+1(12+1)乘以当前位数(100)。

解题思路—精简版:(在牛客网上还看到一个大佬分享的代码,实在是太优美,必须借鉴记录一下)

其实算法核心思想都是一样的,但可能直接看代码会有点懵,在这里贴一小段大佬的解释,看完就能理解

现在说十位数,十位数上出现1的情况应该是10-19,依然沿用分析个位数时候的阶梯理论,我们知道10-19这组数,每隔100出现一次,这次我们的阶梯是100,例如数字317,分析有阶梯0-99,100-199,200-299三段完整阶梯,每一段阶梯里面都会出现10次1(从10-19),最后分析露出来的那段不完整的阶梯。我们考虑如果露出来的数大于19,那么直接算10个1就行了,因为10-19肯定会出现;如果小于10,那么肯定不会出现十位数的1;如果在10-19之间的,我们计算结果应该是k - 10 + 1。例如我们分析300-317,17个数字,1出现的个数应该是17-10+1=8个。
那么现在可以归纳:十位上1出现的个数为:
• 设k = n % 100,即为不完整阶梯段的数字
• 归纳式为:(n / 100) * 10 + (if(k > 19) 10 else if(k < 10) 0 else k - 10 + 1)

Java解题—基础版

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int i = 1, count =0;
        while(n/i!=0){
            int before = n/i/10;
            int after = n%i;
            int current = (n/i)%10;
            if(current==0)
                count += before * i;
            else if(current==1)
                count += before * i + 1 + after;
            else
                count += (before+1) * i;
            i *= 10;
        }
        return count;
    }
}

Java解题—精简版

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        if(n <= 0)
            return 0;
        int count = 0;
        for(long i = 1; i <= n; i *= 10){
            long diviver = i * 10;          
            count += (n / diviver) * i + Math.min(Math.max(n % diviver - i + 1, 0), i);
        }
        return count;
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值