31. 整数中1出现的次数(从1到n整数中1出现的次数)(剑指offer)

关注公众号(落叶归根的猪),获取一手资源~

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

        输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。例如输入12,从1到12这些整数中包含1的数字有1,10,11和12,1一共出现了5次。

1、思路

        两种方法,一种是从1到n遍历,每次通过对10求余数判断整数的个位数字是不是1,大于10的除以10之后再判断。我们对每个数字都要做除法和求余运算以求出该数字中1出现的次数。如果输入数字n,n有O(logn)位,我们需要判断每一位是不是1,那么时间复杂度为O(n*logn)。这样做,计算量大,效率不高。

        本文采用数学之美上面提出的方法,设定整数点(如1、10、100等等)作为位置点i(对应n的各位、十位、百位等等),分别对每个数位上有多少包含1的点进行分析。

        某个数X出现规律: 1~10:1(个位), 1~100:10(十位), 1~1000:100(百位),三个规则:

        R1:如果第i位(左至右)上数字为A(A<X),则第i位上某数X出现次数由更高位决定(若无高位,高位为0),等于更高位数字X当前位数的权重 10^(i-1)

        R2:如果第i位上的数字大于X,则第i位上可能出现X的次数仅由更高位决定(若没有高位,视高位为0),等于(更高位数字+1)X当前位数的权重 10^(i-1)

        R3:如果第i位上的数字为X,则第i位上可能出现1的次数不仅受更高位影响,还受低位影响(若没有低位,视低位为0),等于更高位数字X当前位数的权重 10^(i-1)+(低位数字+1)

        接下来以n=2593,X=5为例来解释如何得到数学公式:

        1 - 2593 中,数字 5 总计出现了 813 次,其中有 259 次出现在个位,260 次出现在十位,294 次出现在百位,0 次出现在千位。

        十位:1 - 2593 中有 259 * 10 + (2591, 2592,2593)因此任意的 X 都出现了 259 次。最后剩余的三个数 2591, 2592 和 2593,因为它们最大的个位数字 3 < X,因此不会包含任何 5。        

        百位:从 1 - 2500 中包含了 25 * 100 +(1 ~ 93),因此任意的 X 都出现了25×10=250次。剩下的数字是从 2501 至 2593,它们最大的十位数字 9 > X,因此会包含全部 10 个 5。最后总计 250 + 10 = 260。(也可以这么看,9>X,则十位上可能出现的X的次数仅由更高位决定,等于更高位数字(25+1)X10^(2-1) = 260)。

        千位:从 1 至 2000 中包含了 2 * 1000 +(1 ~ 593),因此任意的 X 都出现了2×100=200次。剩下的数字是从 2001 至 2593,它们最大的百位数字 5 == X,这时情况就略微复杂,它们的百位肯定是包含 5 的,但不会包含全部 100 个。如果把百位是 5 的数字列出来,是从 2500 至 2593,数字的个数与百位和十位数字相关,是 93+1 = 94。最后总计 200 + 94 = 294。(也可以这么看,5==X,则百位上可能出现X的次数不仅受更高位影响,还受低位影响,等于更高位数字(2)X10^2 +(93+1)=294)。

        千位:现在已经没有更高位,因此直接看最大的千位数字 2 < X,所以不会包含任何 5。

        到此为止,已经计算出全部数字 5 的出现次数:259 + 260 + 294 + 0 = 813

        实例:

        根据设定的整数位置,对n进行分割,分为两部分,高位n/i,低位n%i

        当i表示百位,且百位对应的数>=2,如n=31456,i=100,则a=314,b=56,此时百位为1的次数有a/10+1=32(最高两位0~31),每一次都包含100个连续的点,即共有(a/10+1)*100个点的百位为1

        当i表示百位,且百位对应的数为1,如n=31156,i=100,则a=311,b=56,此时百位对应的就是1,则共有a/10(最高两位0-30)次是包含100个连续点,当最高两位为31(即a=311),本次只对应局部点00~56,共b+1次,所有点加起来共有(a/10*100)+(b+1),这些点百位对应为1

        当i表示百位,且百位对应的数为0,如n=31056,i=100,则a=310,b=56,此时百位为1的次数有a/10=31(最高两位0~30)

        综合以上三种情况,当百位对应0或>=2时,有(a+8)/10次包含所有100个点,还有当百位为1(a%10==1),需要增加局部点b+1

        之所以补8,是因为当百位为0,则a/10==(a+8)/10,当百位>=2,补8会产生进位位,效果等同于(a/10+1) :注意!补多少取决于你要查找的数!!!这只是一个通式。

        总结算法:可以看到,当计算右数第i位包含的 X 的个数时:

            1. 取第i位左边(高位)的数字,乘以10^i−1,得到基础值a。

            2. 取第i位数字,计算修正值

            3. 如果大于 X,则结果为a+10^i−1。

            4. 如果小于 X,则结果为a。

            5. 如果等 X,则取第i位右边(低位)数字,设为b,最后结果为a+b+1。

2. 代码:

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值