剑指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、暴力求解法:这是最容易想到的,即从1到n遍历一遍,对其中的每一个整数都进行求模与取余计算,判断其中的每一位是否为1,并进行相应的计数。
下面是用Java实现的代码供参考:

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int num = 0;
        int temp;
        
        for (int i = 1; i <= n; i++) {
            temp = i;
            while (temp != 0) {
                if (temp % 10 == 1) {
                    num ++;
                }
                temp /= 10;
            }
        }
        return num;
    }
}

2、数学归纳法:
在这里我们任意取一个数来举例说明:比如n=389
百位:该位置上的数字是3(>1),说明之前的百位数为1的数都能取到,所以1的个数是100-199一共有100个;(这里的3是最高位,那么就只有一种可能,可以看做是(0+1)种可能)。
十位:该位置上的数字是8(>1),说明十位上为1的数都可以取到的,而这里需要注意的是,前面 百位上的数字是3,那么百位上的取值可能就是[0,1,2,3],即(3+1)种可能,而每一种可能对应着10-19这10个数,所以1的个数是10*4=40个;
个位:该位置上的数字是9(>1),说明个位上为1的数字是可以取到的,那么前面需要注意前面的数字是38,那么前面的取值就是[38,37,…,1,0],一共39(38+1)种可能,即出现39次1.
综上所述:一共是100+40+39=179次。

还有一些其他情况:比如当前位置上的数字是1,那么就不能只是判断之前位置上的取值可能,还需要判断当前位置之后的取到什么数字:n=216,十位为1,百位为2,那么取值可能是[0,1,2],但是我也需要判断16取到了10-19的什么位置,即16-10+1;即2*10+16-10+1。

再比如当前位置的数字是0,例n=206;那么取值可能是[0,1,2],但是百位取到2的时候是不存在1的,那么就是只有两种可能[0,1],即2*10。

所以综上所述,可能分为三种情况来处理:当前位置是>1,==1,==0。

下面是Java版本的代码实现:

public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int count = 0;
        int temp = n;
        int i = 1;
        while (temp != 0) {
            int mod = temp % 10; //当前位置上的数字
            temp = n / (10 * i); // 当前位置之前的数字
            int mod1 = n % (10 * i); //从当前位置之后的数字,包括当前位置的数字
            if (mod > 1) {
                count += (temp + 1) * i;
            } else if (mod == 1) {
                count += (temp * i + mod1 - i + 1);
            } else {
                count += temp * i;
            }
            i *= 10;
        }
        return count;
    }
}

你可能感兴趣的:(offer刷题)