1的数目
给定一个十进制数N,写下从1到N的所有整数,然后数一下其中出现的所有1的个数。例如:
N=12,那么1,2,3,4,5,6,7,8,9,10,11,12。这样1的个数为5。
问题是:写出一个函数F(N),返回1到N之间出现1的个数,比如F(12)=5。
解法一:
这个问题看上去并不困难,最简单的方法就是从1开始遍历到N,将其中每一个数中含有1的个数加起来,自然而然就得到了从1到N所有1的个数的和。
int Count1InAInt(int n)
{
int num=0;
while(n)
{
if(n%10==1)
num++;
n=n/10;
}
return num;
}
int F1(int n)
{
int ret=0;
for(int i=1;i<=n;i++)
ret+=Count1InAInt(i);
return ret;
}
这个方法很简单,实现也很简单,容易理解。但是这个算法的效率是致命问题。时间复杂度为O(N*lgN),如果给定N=100000000,那么算出F(N)的值大概需要30秒的时间。
看起来要计算1到N的数字中所有1的和,至少也得遍历1到N之间所有的数字才能得到。那么有没有快一点的方法来解决这个问题呢?
解法二:
仔细分析这个问题之后,给定了N,似乎可以通过分析小于N的数在每一位上可能出现1的次数之和来得到这个结果。
例如:F(13)=十位出现1的个数+个位出现1的个数=4+2=6;
下面我们推导一般情况下,从N到F(N)的计算方法:
假设N=abcde,这里a,b,c,d,e分别是十进制数N的各个数位上的数字。如果要计算百位上出现1的个数,它将会受到3个因素的影响:百位上的数字,百位以下的数字(低位),百位以上的数字(高位)。
如果百位上的数字为0,可知,百位上出现1的次数由更高位决定,比如12023,百位上出现1的情况是:100~199,1100~1199....11100~11199,一共有1200个。也就是由更高位数字12决定,并且等于更高位数字12*当前的位数(100)。
如果百位上的数字为1,可知,百位上出现1的次数不仅受更高位影响,还受低位的影响。例如对于12123,受高位影响,百位上出现1的情况是:100~199,1100~1199....11100~11199,一共有1200个,等于更高位数字12*当前的位数(100)。但是它还受低位数影响,百位上出现1的情况是:12100~12123,一共124个,等于低位数(123)+1。
如果百位上数字大于1(2,3…9),则百位上出现1的情况仅受更高位的影响。例如:12223,则百位上出现1的可能情况为:100~199,1100~1199....11100~11199,12100~12199,一共1300个,并且等于更高位数字加一,再乘以当前的位数,即(12+1)*100。
int F2(int n)
{
int ret=0;
int factor=1;
int low=0;
int cur=0;
int high=0;
while(n/factor!=0)
{
low=n-(n/factor)*factor;
cur=(n/factor)%10;
high=n/(factor*10);
switch (cur)
{
case 0:
ret+=high*factor;
break;
case 1:
ret+=high*factor+low+1;
break;
default:
ret+=(high+1)*factor;
break;
}
factor*=10;
}
return ret;
}