整数中1出现的次数(从1到n整数中1出现的次数)

1.本题知识点

   时间效率

2. 题目描述

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

3. 思路

   这就是一套数学题啊,找数字的规律。看了剑指offer和网上一些思路,有点混乱,直到看了一个github大神写的极简思路,才感觉豁然开朗。具体分析如下:
   总的来说,就是通过求每个位上1出现次数 再累加起来。
   我们拿一个5位数的百分位 (其它位同理) 举例,一种有3种情况:
   ① 百分位数字为0时,比如31056,百分位切割后:a = 310 , b = 56
  整数中1出现的次数(从1到n整数中1出现的次数)_第1张图片
   ② 百分位数字>=2时,比如31256,百分位切割后:a = 312 , b = 56
  整数中1出现的次数(从1到n整数中1出现的次数)_第2张图片
   ③ 百分位数字为1时,比如31156,百分位切割后:a = 311 , b = 56
  整数中1出现的次数(从1到n整数中1出现的次数)_第3张图片
   统一一下表达式,把百分位为0 和 >=2两种情况合并,因为0和>=2的表达式区别在于a/10是否+1,也就是说产生进位就+1,否则不+1,由于a/10是取商操作,充分利用这个性质,表达式合并为[(a+8)/10] * 100 。
   进一步分析合并表达式:当百分位>=2时a+8产生进位,表达式相当于(a/10 + 1)*100,当百分位=0时,不产生进位,表达式相当于(a/10)*100 。
   最后,表达式整理为:在这里插入图片描述
   百分位代码表示为:(a+8)/10 * 100 + (a % 10 == 1 ? b+1 : 0)
   千分位代码表示为:(a+8)/10 * 1000 + (a % 10 == 1 ? b+1 : 0)
   万分位代码表示为:(a+8)/10 * 10000 + (a % 10 == 1 ? b+1 : 0)
   十分位代码表示为:(a+8)/10 * 10 + (a % 10 == 1 ? b+1 : 0)
   个分位代码表示为:(a+8)/10 * 1 + (a % 10 == 1 ? b+1 : 0)
   总的次数就是把它们加起来即可。
   Java 版:
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        //校验
        if(n <= 0) return 0;
        
        int count1 = 0;
        //从个位数开始,按照我们分析的表达式计算次数
        for(long q = 1; q <= n; q*=10){
            long a = n/q, b = n%q; //按位数切割为a b, 从个位开始
            count1 += (a+8)/10 * q + (a % 10 == 1 ? b+1 : 0);
        }
        
        return count1;
    }
}

参考 1

你可能感兴趣的:(数据结构和算法)