1的数目

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;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值