统计1的个数

leetcode第233题:

给定一个整数 n,计算所有小于等于 n 的非负整数中数字 1 出现的个数。

解法1:暴力

题目中让计算所有小于n的非负整数的出现数字1的个数,那就将每个数字中1的个数都统计数来,然后相加,就得出了所有1的个数。这样思路很简单,代码更简单。下面是实现:

int countDigitOne(int n)
{
	int count = 0;
	for (int i = 1; i <= n; ++i)
	{
		int num = i;
                //分解每一个数,统计1的个数
		while (num)
		{
			if (num % 10 == 1)
			{
				++count;
			}
			num = num / 10;
		}
	}
	return count;
}

这个算法的时间复杂度为O(n^2)。一旦数值过大,消耗的时间就会非常多。

解法2:寻找规律。

将1的总数记为count。分情况讨论。

n为个位数时:

        当个位为0时:0

        当个位大于等于1时:1

n为十位数时:

我们可以分步计算1在个位上出现的次数,1在10位上出现的次数。

1在个位上出现的次数:

        当个位为0时:出现次数为十位上的数字(十位增加一位,个位就需要从0-9循环一次。)

        当个位大于等于1时:出现次数为十位上的数字+1。

1在10位上出现的次数:

         当十位为0时:1出现的次数为0。

         当十位为1时:1出现的次数为(个位的数值加上1)。如:11(1+1=2),包括:10,11。

         当十位大于1时:1出现的次数10。只会在10-19的十位出现1。

所以1在十位数中出现的次数的规律为:

         当十位为0时:个位上1出现的次数。

         当十位为1时:(1)个位为0,1+1=2。(2)个位大于等于1,个数数字+1+个位的1+多循环的1。

         当十位大于1时:(1)个位为0,10+十位上的数字。(2)个位大于等于1,10+十位的数字+多循环的1。

例如:

n=12,1出现的次数为(2+1+1+1=5)

n=95,1出现的次数为(10+9+1=20)

n为百位数时:

规律和之前10位上的差不多,我们举一个列子。假设n=625时,计算1出现的个数。

1在个位上出现的次数:62*1+1=63. 1==>10^0

高位为62说明个位一共循环了62次,个位为5,说明还循环了一次0-5,也出现了1次1.即为62*1+1.

1在十位上出现的次数:6*10+10=70.  10==>10^1

高位为6,说明十位一共循环了6次,因为十位循环一次1会出现10次,十位为2,还循环了一次0-2,6*10+10.

1在百位上出现的次数:100.     100==>10^2

高位为0,说明循环了0次,因为百位循环一次1会出现100次,百位为6,还循环了一次0-6. 0*100+100。

所以1在625上出现的次数位:63+70+100=233.

通过上述的分析我们可以发现如下规律:

1在n的第i为出现的次数为:

m为n的第i位的高位表示的数字,k表示第i位低位表示的数字。
 

第i位的数字为0:m*(10^(i-1))

第i位的数字为1: m*(10^(i-1))+k+1

第i位数字大于1:m*(10^(i-1))+10^(i-1)

我们将10^(i-1)记为t,可得:

第i位的数字为0:mt

第i位的数字为1: mt+k+1

第i位数字大于1:mt+t

我们取3105验证一下:

1在个位出现的次数:i=1,m=310,k=0,              310*(10^(i-1))+10^(i-1)=310+1=311

1在十位出现的次数:i=2,m=31,k=5,             31*(10^(i-1))=31*10=310

1在百位上出现的次数:i=3,m=3,k=5,         3*(10^(i-1))+5+1=3*100+6=306

1在千位上出现的次数:i=4,m=0,k=105,  0*(10^(i-1))+10^(i-1)=0*1000+1000=1000

所以1在3105中出现的次数为:311+310+306+1000=1927.

代码实现:

int countOne(int n)
{
	int count = 0;
	//m:第i位的高位
	//k:第i位的低位
	int i = 1, m = n / 10, k = 0;
	int num = n,w;
	while (num)
	{
		count = count + m*int(pow(10.0, i-1));
		w = num % 10;
		if (w == 1)
		{
			count = count + k + 1;
		}
		else if (w > 1)
		{
			count = count + int(pow(10.0, i-1));
		}
		num = num / 10;
		m = m / 10;
		i++;
		k = n % int(pow(10.0, i - 1));
	}
	return count;
}

注意输入数据的范围。

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值