剑指offer题目31:从1到n整数中1出现的次数

题目描述

输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数,例如输入13,1~13中包含1的数字有1、10、11、12、13因此共出现6次。

解答思路

这道题目对我来说也挺难的,需要做的就是画出例子来分析。

如数字21345。
我们先分别统计每位出现1的次数然后累加起来。

个位数

我们注意到每循环10次数,必定出现1次1,如0 - 9,出现了1, 再如10-19,11里的个位也出现了1。
那么我们可以得出如下公式:

sum = n / 10 * 1;  

但是注意到最后的的区间一般是不完整的,例如数字23,有0-9,10-19,20-23。最后余3,如果这个数是大于等于1的话,也是可以算是1次的。

k = n % 10;  
sum = n / 10 * 1 + (k >= 1 ? 1 : 0);

十位数

同理分析,每循环100次,必定出现10次,如10-19,110-119。

sum = n / 100 * 10;

同理注意到可最后的区间,如数字115,偏移量15,有区间110-115。则出现1的次数是15-10+1=6次。如数字119,出现1的次数是10。

k = n % 100;   
if(k >= 19)   
    offset = 10;
else if (k >= 10)
    offset = k - 10;

百位数

同理分析,每循环1000次,必定出现100次,如100-199,1100-1199,2100-2199。

sum = n / 1000 * 100;

同理注意到可最后的区间,如数字1115,有区间1100-1115。则出现1的次数是1115-1100+1=16次。

k = n % 1000;   
if(k >= 199)   
    offset = 100;
else if (k >= 100)
    offset = k - 100;

实现代码

class Solution {
public:
    int NumberOf1Between1AndN_Solution(int n)
    {
        int sum = 0;
        for (int i = 1; i <= n; i*= 10)
        {
            int diviver = i * 10;
            sum += n / diviver * i;

            // 计算剩余阶梯的
            int k = n % diviver;

            if(k >= i * 2 - 1) {
                sum += i;
            } else if(k >= i) {
                sum += k - i + 1;
            }
        }
        return sum;
    }
};
posted @ 2019-04-08 15:15  {-)大傻逼  阅读(218)  评论(0)    收藏  举报
欢迎转载,转载请注明本文地址。