本博客主要内容为图书《剑指offer》43 题的解题思路及代码。方法可能还有不足之处,欢迎大家讨论评论。
1. 题目描述
输入一个整数 n ,求出 1~n 这 n 个整数的十进制表示中 1 出现的次数。例如输入 12,1 ~ 12 这些整数中包含 1 的数字有 1 ,10,11,12,“1 ”一种出现了 5 次。(注意其中 11 中 1 出现了两次)
2. 解题思路
2.1 最高位为 1 的情况
对于某一个数字,如 1234 ,考虑 1~1234 中 1 一共出现了多少次的时候,可以进行如下分解:
这个时候只需要分 1 : 999 和 1000 : 1234 两步进行考虑就行。
对于第一部分,我们可以观察到一个比较强的规律。当数字是一位数的时候,数字的长度
length=1
l
e
n
g
t
h
=
1
,也就是说 1~9 只包含 1 个 1,即
当数字是二位数的时候,数字的长度 length=2 l e n g t h = 2 ,我们可以将 1~99在分解为三个部分,即
其中 1~9 的结果为 f(length)=1 f ( l e n g t h ) = 1 , 10~19 一共有十个数(二十个数字),其中十位都是1 ,有 10 个1;剩下 10个个位的情况是 0~9 中有多少个 1 ,这个情况与 1~9 的结果是相同的;20~99有 (9−2+1) ( 9 − 2 + 1 ) 个1,即
通过进行归纳可以得到下面的通式
其中的 length 是对应数字的长度。
对于第二部分,即 1000:1234 1000 : 1234 ,其中最高位重复了 1234−1000+1 1234 − 1000 + 1 次,即 n−10length−1+1 n − 10 l e n g t h − 1 + 1 ,然后考虑除了最高位剩下位(0~234)中 1 出现的次数,即计算 g(n−10length−1) g ( n − 10 l e n g t h − 1 ) 。
所以数字最高位为 1 的情况的计算表达式如下:
其中的 f(length)=10n−1+10f(length−1) f ( l e n g t h ) = 10 n − 1 + 10 f ( l e n g t h − 1 ) ,n 是输入的数字,length 是输入数字的长度。
2.2 最高位>1 的情况
对于某一个数字,如 4234 ,考虑 1~4234 中 1 一共出现了多少次的时候,可以分解如下的四个部分:
其中 1:999 可以通过 f f 计算得到;1000:1999 中最高位出现的次数为 ,而剩下的位数出现 1 的总次数为 f(length−1) f ( l e n g t h − 1 ) ;2000:3999中最高位不会出现 1 ,剩下的位出现 1 的问题又变成了 1:999,即 (3−2+1)f(length−1) ( 3 − 2 + 1 ) f ( l e n g t h − 1 ) ,最后剩下 4000:4234 ,而这个问题等价为 0:234 中有多少个 1 的问题,可以通过 g(n−a∗10length−1) g ( n − a ∗ 10 l e n g t h − 1 ) 来解决。所以此时的通式为
其中的 a 是最高位的数字
2.3 情况总结
所以计算这个问题的递推公式如下:
其中
所以总体的思想就是,根据输入数字的最高位对情况进行分类,之后带入上面的递推公式,进行递归操作。递归的停止条件为剩下的数字的长度为 1,如果剩下的数字是0返回0,否则返回1.
3. 代码实现
# -*- coding:utf-8 -*-
class Solution:
def NumberOf1Between1AndN_Solution(self, n):
# 如果数字为 0 则返回 0
if not n:
return 0
# 将数字转化为字符串,得到数字的长度
strN = str(n)
length = len(strN)
# 通过 f 函数计算递推通式中的 f (length -1)
mydicdictionary = self.dictionary(length-1)
#如果长度为 1 且不为 0 话就返回 1
if length == 1:
return 1
# 求出当前数字的最高位数字
temp = 10**(length-1)
a = n//temp
# 根据判断结果执行递推公式
if n-temp >= temp:
return temp + a*mydicdictionary + self.NumberOf1Between1AndN_Solution( n-a*temp)
else:
return mydicdictionary + n-temp + 1 +self.NumberOf1Between1AndN_Solution( n-temp)
def dictionary(self,length):
mydicdictionary = [0]
for i in range(1 , length+1):
result = 10**(i-1) + 10*mydicdictionary[i-1]
mydicdictionary.append(result)
return mydicdictionary.pop()