剑指offer43 1~n整数中1出现的次数
首先暴力解法,遍历1~n,单独写个函数统计每个数字中1出现的次数,最后相加即可。
数字n有logn位,故时间复杂度O(nlogn)。
但其实这个问题是有数学规律的。
我们也以剑指offer上的数 21345为例,来看看有什么规律。
首先,10000~19999,万位的1有10000个。也即0~21345万位的1有10000个。
然后我们把0~21345分为0~1345,1346~11345,11346~21345三段。
后两段中,后四位出现1的次数应该为$2*C_4^1*10^3=8000$。因为这个可以看作一个排列问题:四位数字中出现1的排列。然后对于重复的数字,比如1101,本应该去重只计算1次,但是由于我们算的是出现1的次数,1101出现3次,而我们在千、百、个位各计一次是正好的。所以问题转化为:四位分别是1时,其他位随便排列,记一次。
如果还不理解,可以看2位数的情况,答案应为2*10 = 20。
10~109中,忽略百位的1,出现1的有:
10,11,12,13,14,15,16,17,18,19,
21,31,41,51,61,71,81,91,101 共20个1.
第一行只计十位的1,第二行只计个位的1的话:
10,11,12,13,14,15,16,17,18,19,
11,21,31,41,51,61,71,81,91,101 正好20个数。
然后0~1345可以递归使用以上方法。
public class Solution { public int NumberOf1Between1AndN_Solution(int n) { if(n<=0) return 0; int temp = n; int ndigit = 0; while(temp!=0){ temp/=10; ndigit++; } return getNumber(n,ndigit); } private int getNumber(int n,int ndigit){ if(ndigit<1) return 0; if(n==0) return 0; int firstDigit = n/(int)Math.pow(10,ndigit-1); int count = 0; if(firstDigit>1){ count+=(int)Math.pow(10,ndigit-1); }else{//firstDigit==1 count+=n%(int)Math.pow(10,ndigit-1)+1; } if(ndigit>1) count+=firstDigit*(ndigit-1)*(int)Math.pow(10,ndigit-2); return count+getNumber(n%(int)Math.pow(10,ndigit-1),ndigit-1); } }
运行时间:29ms
占用内存:9244k
# -*- coding:utf-8 -*- class Solution: def NumberOf1Between1AndN_Solution(self, n): # write code here if n<1: return 0 digit = 0 temp = n while temp>0: temp=temp//10 digit+=1 return self.helper(n,digit) def helper(self,n,digit): if digit<=0 or n==0: return 0 ans = 0 firstDigit = n//(10**(digit-1)) if firstDigit==1: ans = n%(10**(digit-1))+1 else: ans = 10**(digit-1) ans += firstDigit*(digit-1)*(10**(digit-2)) return ans+self.helper(n%(10**(digit-1)),digit-1)
运行时间:24ms
占用内存:5860k

浙公网安备 33010602011771号