剑指Offer:整数中1出现的次数(从1到n整数中1出现的次数)Java/Python

1.题目描述

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

2.算法描述

方法1(投机):

遍历1-n所有数字,将数字转换为字符数组。统计字符数组中1的个数。得到答案。

方法2(推荐):

分别去看个、十、百、千、万等等位置上分别出现1的次数,最后相加得到总次数。
难点是, 如 何 找 到 某 一 个 数 位 上 1 出 现 的 次 数 呢 ? \red{如何找到某一个数位上1出现的次数呢?} 1
下面以123x456为例子,来讲解123x456中千位1出现的次数。其他位上的1与此是类似的。
1.首先将123x456分为两个部分:
a = 123 x 456 / 1000 = 123 x a = 123x456/1000=123x a=123x456/1000=123x
b = 123 x 456 % 1000 = 456 b = 123x456\%1000 = 456 b=123x456%1000=456

1)如果 x > = 2 \red{x>=2} x>=2

x = 1 x=1 x=1出现的次数有 ( a / 10 + 1 ) = 123 + 1 = 124 (a/10+1)=123+1=124 (a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现 1000 1000 1000个,哪 1000 个 1 呢 ? _ 1000 − _ 1999 。 其 中 _ 可 以 填 0 − 123 中 的 任 何 一 个 \red{1000个1呢?\_1000-\_1999。其中\_可以填0-123中的任何一个} 10001_1000_1999_0123
故: x > = 2 x>=2 x>=2时,千位上出现1的总次数 = ( a / 10 + 1 ) ∗ 1000 =(a/10+1)*1000 =(a/10+1)1000

2)如果 x = 1 \red{x=1} x=1

x = 1 x=1 x=1出现的次数有 ( a / 10 + 1 ) = 123 + 1 = 124 (a/10+1)=123+1=124 (a/10+1)=123+1=124, 即千位以上的数可以是0-123中的任何一个,在当中任取一个作为千位以上的数,1都会出现 1000 1000 1000个, 但 是 , 当 取 123 时 , 1 不 会 出 现 1000 次 , 因 为 1231456 已 经 到 了 最 大 的 数 字 了 , 而 是 只 会 出 现 ( 456 + 1 ) 个 1 , 即 _ 1000 − _ 1456 , 其 中 _ 为 123 \red{但是,当取123时,1不会出现1000次,因为1231456已经到了最大的数字了,而是只会出现(456+1)个1,即\_1000-\_1456,其中\_为123} ,123110001231456,456+11_1000_1456,_123
故: x = 1 x=1 x=1时,千位上出现1的总次数 = ( a / 10 ) ∗ 1000 + ( b + 1 ) =(a/10)*1000 + (b+1) =(a/10)1000+(b+1)

3)如果 x = 0 \red{x=0} x=0

x = 1 x=1 x=1出现的次数有 a / 10 = 123 a/10=123 a/10=123,即千位以上的数可以是0-122中的任何一个,在当中任取一个作为千位以上的数,1都会出现 1000 1000 1000个,哪 1000 个 1 呢 ? _ 1000 − _ 1999 。 其 中 _ 可 以 填 0 − 122 中 的 任 何 一 个 , 当 _ 为 123 时 , x 只 能 是 0 了 \red{1000个1呢?\_1000-\_1999。其中\_可以填0-122中的任何一个,当\_为123时,x只能是0了} 10001_1000_1999_0122_123x0

综上,我们可以得出以下的一半性结论:

初始化ans=0;
将base依次取 1 , 10 , 100 , 1000 , 10000... 1,10,100,1000,10000... 1101001000,10000...,按照: a n s = a n s + ( a + 8 ) / 10 ∗ b a s e + ( a % 10 = = 1 ? ( b + 1 ) : 0 ) ans =ans+(a+8)/10 * base+(a\%10==1?(b+1):0) ans=ans+(a+8)/10base+(a%10==1?(b+1):0),将出现的1,依次加到最终答案中。遍历完成就得到了最终1出现的次数。
其中(a+8)刚好可以处理以上的三种情况,只有在base位上是 > = 2 >=2 >=2的时候才会进位,而base位上为1或0,则不会进位。

3.代码描述

3.1.Java代码

//方法2
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int ans = 0;
        for(int base = 1; base<=n; base*=10){
            int a = n /base;
            int b = n % base;
            ans += (a+8)/10 * base + (a%10==1?(b+1):0);
        }
        return ans;
    }
}
//方法1(不推荐)
public class Solution {
    public int NumberOf1Between1AndN_Solution(int n) {
        int ans = 0;
        for(int i=1; i<=n; i++){
            char[] chars = (i+"").toCharArray();
            for(char c: chars)
                if(c == '1')
                    ans++;
        }
        return ans;
    }
}

3.2.Python代码

#方法2
# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        ans = 0
        base = 1
        while base<=n:
            a, b = n/base, n%base
            ans += (a+8)/10*base + (a%10==1)*(b+1)
            base *=10
        return ans
#方法1(不推荐)
# -*- coding:utf-8 -*-
class Solution:
    def NumberOf1Between1AndN_Solution(self, n):
        # write code here
        ans = 0
        for i in range(1,n+1):
            s = str(i)
            for c in s:
                if c == '1':
                    ans += 1
        return ans
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值