《剑指offer》:[32]从1到n整数中1出现的次数

来源:互联网 发布:美元霸主地位知乎 编辑:程序博客网 时间:2024/05/22 03:33
题目:输入一个整数n,求从1到n这n个整数的十进制表示中1出现的次数。
例如输入12,从1到12共有1,10,11,12这四个数有1出现,且出现为5次。

方案一:对每一个数字进行统计1的个数,然后相加。统计的方法就是对数取余法,看个位数上是否为1,。如果数大于10,则除10再取余数,看是否为1.该方法的缺点是:如果输入的n比较大,则计算量比较大。对于N个数,每个数有logN位,则其时间复杂度为O(N*logN)。比较耗时。
方案二:采用递归的方法。对于N位的数字,使其时间复杂度为O(logN)。
由于方案一对大量数据的时间效率低,所以我们采取另一种方案,不用单个统计每个数字的1的个数。而是观察数字的特点:
例如:输入数字:22345。
我们注意到:其实可以将数字分为两段,1-2345,2346-22345。
先看2346-22345中1出现的个数:
第一步:首先看1出现在最高位的情况:10000-19999,这里刚好是10000次;
但是也不全是:如235-1234,这里1出现在高位时的次数为:234+1=235次;而不是1000次。此时当1出现在最高位时:其次数为出去最高位后剩下的数字+1即为所得;
第二步:除了1在最高位以外的其它4个位置:2346-22345中,由于最高位是2,所以可以分为两段,2346-12345,12346-22345;每一段剩下的四位中都可以选择一位是1,其他三位中在0-9中任选一位,根据排列组合其出现的次数为:2*4*10^3=8000次。
注意这里不要和排列组合中的数字搞混了,这里是求1的个数,不是求的排列组合的数字的个数!
第三步:最后我么再看1-2345;这段其实和上一段分析的原理一样,既然一样,那么我们就可以用递归来实现了!
总结一下:
(1)当1出现在高位:当最高位=1时,去掉高位1剩下的数字+1即为所求次数;当最高位>1时,次数为:10^(length-1);
(2)除高位剩下的位置中1出现的个数:FirstData*(length-1)*10 ^(length-2);

方案一具体实现代码如下:
#include<iostream>using namespace std;bool IsInvalid=false;int NumberOf1(unsigned int number){int n=0;while(number){if(1==number%10)n++;number/=10;}return n;}int NumberOf1BetweenN(unsigned int number){if(number<0){IsInvalid=true;return 0;}int count=0;for(int i=1;i<=number;i++)count+=NumberOf1(i);return count;}int main(){int result=NumberOf1BetweenN(12);if(!IsInvalid)cout<<"1的个数:"<<result<<endl;elsecout<<"THE INPUT IS INVALID!"<<endl;system("pause");return 0;}

运行结果:


方案二实现代码:
#include<iostream>#include <stdlib.h>using namespace std;bool IsInvalid=false;int PowerBase(unsigned int n){int result=1;for(unsigned int i=0;i<n;i++)result*=10;return result;}int NumberOf1(char *strr){if(NULL==strr || *strr<'0'||*strr>'9'||*strr=='\0'){IsInvalid=true;return 0;}int first=*strr-'0';unsigned int length=static_cast<unsigned int>(strlen(strr));if(length==1 && first==0)return 0;if(length==1 && first>0)return 1;int NumFirstDigital=0;//计算最高位1的个数;if(first>1)NumFirstDigital=PowerBase(length-1);else if(first==1)NumFirstDigital=atoi(strr+1)+1;//计算除最高位1以外的各位1出现的个数int NumOtherDigital=first*(length-1)*PowerBase(length-2);//计算前一段的1的个数;递归调用。int NumRecursive=NumberOf1(strr+1);return NumFirstDigital+NumOtherDigital+NumRecursive;}int NumberOf1BetweenN(unsigned int number){if(number<=0){IsInvalid=true;return 0;}char str[50];itoa(number,str,10);return NumberOf1(str);}int main(){int result=NumberOf1BetweenN(21345);if(!IsInvalid)cout<<"1的个数:"<<result<<endl;elsecout<<"THE INPUT IS INVALID!"<<endl;system("pause");return 0;}

运行结果:




1 0
原创粉丝点击