本博客主要內容爲圖書《劍指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 兩步進行考慮就行。
對於第一部分,我們可以觀察到一個比較強的規律。當數字是一位數的時候,數字的長度 ,也就是說 1~9 只包含 1 個 1,即
當數字是二位數的時候,數字的長度 ,我們可以將 1~99在分解爲三個部分,即
其中 1~9 的結果爲 , 10~19 一共有十個數(二十個數字),其中十位都是1 ,有 10 個1;剩下 10個個位的情況是 0~9 中有多少個 1 ,這個情況與 1~9 的結果是相同的;20~99有 個1,即
通過進行歸納可以得到下面的通式
其中的 length 是對應數字的長度。
對於第二部分,即 ,其中最高位重複了 次,即 ,然後考慮除了最高位剩下位(0~234)中 1 出現的次數,即計算 。
所以數字最高位爲 1 的情況的計算表達式如下:
其中的 ,n 是輸入的數字,length 是輸入數字的長度。
2.2 最高位>1 的情況
對於某一個數字,如 4234 ,考慮 1~4234 中 1 一共出現了多少次的時候,可以分解如下的四個部分:
其中 1:999 可以通過 計算得到;1000:1999 中最高位出現的次數爲 ,而剩下的位數出現 1 的總次數爲 ;2000:3999中最高位不會出現 1 ,剩下的位出現 1 的問題又變成了 1:999,即 ,最後剩下 4000:4234 ,而這個問題等價爲 0:234 中有多少個 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()