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

题目:输入一个整数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;
	else
		cout<<"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;
	else
		cout<<"THE INPUT IS INVALID!"<<endl;
	system("pause");
	return 0;
}

运行结果:




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值