1的数目

前言

国庆7天长假,自己还是要有学习计划的,前两天需要找回之前的状态,预计10道ACM题目和看完《cracking the coding interview》。话说,还算顺利的拿到了阿里的系统工程师offer,虽然没最终确定,估计问题不大了,毕竟短信都收到了,很开心却让我丧失了很多的动力,趁着国庆长假找回失去的状态,记得刚读研就是利用国庆长假看完《php和myslq开发》,从而走上了开发服务器端应用层API的道路

题目

Write a method to count the number of 2s between 0 and n.

思路一

首先,最简单的思路就是从0到n计算每个数出现2的个数。时间复杂度为0(nlogn)

/**
 * 暴力法求解
 *
 * T = O(nlogn)
 *
 */
int count2(int n)
{
	int count = 0;

	while (n) {
		if (n % 10 == 2)	count ++;

		n /= 10;
	}

	return count;
}

int calViolentTwo(int n)
{
	int i, num;

	for (i = 0, num = 0; i <= n; i ++) {
		num += count2(i);
	}

	return num;
}

上述代码最大的问题还是效率,当32bit机器,n为最大正整数2147483647,求1到n之间的2出现的数量耗时相当长久。因此,想提高效率,需要从数字中找规律,参考《编程之美》,顺便吐槽一下校招的笔试和面试题目,大部分都是《剑指offer》和《编程之美》上的,还好我都看完了


思路二

假设一个5位数abcde,我们以百位上的数字为例,即从0到abcde的数中,有多少个数的百位上是2.

当百位c为0时,例如13023,0到43023中哪些数的百位会出现2?我们从小的数开始算起,200-299,1200-1299,2200-2299,....,12200-12299,这1200个数,它们的百位为2,共1300个数,再往下,13200已经大于13023了,因此不再往下。所以当百位为0时,百位出现2的次数只由更高位决定,等于更高数位(13)×当前位数(100) = 1300个

当百位为1时,例如13123,分析跟百位为0是一样的

以上两步综合起来,可以得到以下结论:

当某一位数字小于2时,那么该位出现2的次数为:更高位数字×当前位数

当百位数字为2时,例如13223,那么除了200-299到12200-12299这1300个数外,还有13200-13223,这24个数(低位数字+1).所以,当百位数字为2时,百位出现的次数既受高位影响也受低位影响,结论如下:

当某一位数字等于2时,那么该位出现2的次数为:更高数字×当前位数+低位数字+1

当百位数字大于2时,例如13523,那么固定低位200-299,高位依次从0到13,更1400个数字,因此出现2的次数为:(高位数字+1)×当前位数,结论如下:

当某一位数字大于2时,那么该位出现2的次数为:(更高位数字+1) × 当前位数

根据以上三条结论,可以写出如下代码:

/**
 * 数学方法
 *
 * T = O(lnN/ln10)
 */ 
int calTwo(int n)
{
	int low, high, cur, factor = 1, num = 0;

	while (n / factor) {
		cur = (n / factor) % 10;
		low = n - (n / factor) * factor;
		high = n / (factor * 10);

		switch (cur) {
			case 0 :
			case 1 :
				num += high * factor;
				break;
			case 2 :
				num += high * factor + low + 1;
				break;
			default :
				num += (high + 1) * factor;
				break;
		}

		factor *= 10;
	}

	return num;
}


你可能感兴趣的:(1的数目)