剑指offer -- 1~n 整数中1出现的次数(普通解法+找规律解法)

题目:
输入一个整数 n ,求1~n这n个整数的十进制表示中1出现的次数。
例如,输入12,1~12这些整数中包含1 的数字有1、10、11和12,1一共出现了5次。

方法一:
看完题目以后我们能想到的最直观的解法就是累加1~n中每个整数1出现的次数,通过每一个数的对10求余数,来判断他个位是否为1,如果大于10则除以10后再判断。代码很简单,如下。

int Number_Of_Between_1_And_N(int n)
{
    
    
	int number = 0;
	for(int i = 1;i <= n;i++)
		number = number + Number_Of_1(i);
	return number;
}

int Number_Of_1(int n){
    
    
	int number = 0;
	while(n)
	{
    
    
		if(n%10 == 1)
			number++;
		n = n/10;
	}
	return number;
}

方法二:
在上述方法中,当我们输入的n非常大的时候,需要大量的计算,运算效率不高,接下里让我们看一看能否在数字上找到1存在的规律,为了方便,我们不妨取一个较大的数字如21345。

我们把1 ~ 21345的所有数字分成两段,一段是1 ~ 1345;另一段是1346 ~ 21345 。

我们先看1346 ~ 21345中1出现的次数,1出现分两种情况,首先分析1出现在最高位的情况(本例中是万位),在1346~21345的数字中,1出现在万位是在10000 ~ 19999这一万个数中,一共出现了10000(10的4次方)次。值得注意的是,并不是对所有的5位数而言在万位出现的次数都是10000次,对于万位是1的数字来说,1只出现在10000 ~ 12345 的万位,出现的次数不是10的四次方次,而是除去最高位加1次,也就是2345+1次。

接下来分析1出现在除最高位之外的其他4位数的情况中,1346~21345,由于最高位是2,我们把这段数字在分成两段,1346 ~ 11345,11346 ~ 21345,先来看前一段 1346 ~ 11345 ,由于 10001 ~ 11345的万位已经是1固定,我们可以忽略它看成是0001 ~ 1345,前面还剩下1346 ~ 10000,可以看成是0001 ~ 10000,4位数选择其中1位为1,其余三位可以在0 ~ 9这10个数字中任意选择,因此为4 * 10 * 10 * 10次,11346 ~ 21345同理。

至于1 ~ 1345中1出现的次数可以用递归求得,这也是我们为什么要把1~21345 分成 1 ~ 1345和1346 ~ 21345两段的原因,因为把21345的最高位去掉就变成了1345,便于我们采用递归的思路。

int Number_Of_Between_1_And_N(int n)
{
    
    
	if(n<=0)
		return 0;
	char strN[50];
	sprintf(strN,"%d",n);

	return Number_Of_1(strN);
}

int Number_Of_1(const char *strN)
{
    
    
	if(!strN || *strN<'0'||*strN>'9'||*strN == '\0')
		return 0;

	int first = *strN - '0';
	int length = strlen(strN);

	if(length == 1&& first == 0)
		return 0;

	if(length == 1&&first >1)
		return 1;


	//假设strN是“21345”  numFirstDigit 为万位1的次数  
	int numFirstDigit = 0;                                                             
	if(first>1) 
		numFirstDigit = PowerBase10(length-1);
	else if(first == 1) //最高位为1
		numFirstDigit = atoi(strN +1)+1;

	//numOtherDigits 是1346~21345除最高位之外的数位中1的数目  

	int numOtherDigits = first *(length-1)*PowerBase10(length-2);

	//numRecursive 是1~1345中的数目

	int numRecursive = Number_Of_1(strN+1);

	return numFirstDigit + numOtherDigits +numRecursive;
}
int PowerBase10(int n){
    
    
	int result = 1;
	for(int i=0;i<n;i++)
		result = result*10;

	return result;
}

猜你喜欢

转载自blog.csdn.net/scarificed/article/details/120587385