1 ~ n 整数中 1 出现的次数

题目描述: 输入 一个整数,求 1~n 这 n 个整数的十进制表示中1 出现的次数。

例如输入 12 那么 1~12 这些整数中包含 1 的数字有 1  10  11  12 ,一共出现了 5 次


思路:

最简单的思路:从1 开始遍历到 n ,统计所有数字出现 1 次数的总和。如何统计一个数字出现 1 的次数?

我们可以每次对该数模 10 ,比较余数是否等于1 ,然后将该数除以10 。统计一个数出现几次 1 的时间复杂度为logn,

所有总复杂度为 nlogn.

具体代码:

	//时间复杂度为 nlogn 
	public static int numberOfKBetween1AndN(int n,int k){
		int num = 0;
		for(int i = 1; i <= n; i++){
			num += numberOf1(i,k);
		}
		return num;
	}
	private static int numberOf1(int n,int k) {
		// TODO Auto-generated method stub
		int num = 0;
		while(n > 0){
			if(n%10 == k)
				num++;
			n = n/10;
		}		
		return num;
	}


但是时间复杂度为 nlogn ,任然不是最好的。

我们可以根据数字规律来做道题,时间复杂度只需要 logn 。

一个数字(0 除外)可以出现在个位、十位、百位、千位......我们可以统计出它在各个位出现的次数。

以一个实际的例子来找出它的数字规律,比如现在输入一个数 15292。那么1~15292 中国 2 出现的次数是6653.

我们首先看个位,将个位的 2 去掉,变成了 1529,从 1 ~ 1529,一共有1529个数,所以此时出现1529次2,又因为  

个位刚好为 2 ,所以应该再加上 1 ,即个位上出现 2 的次数为 1530.

接下来看 十位,1529去掉 9 变成 152 ,所以出现 1520次(因为还有个位,所以应该将152乘以 10),又因为 9 > 2 ,所以出现的次数为 1520 + 10 = 1530 次。

接着看 百位,把 2 去掉,变成 15,所以出现的次数为 1500次( 有个位和十位,所以要乘以100),此时 百位刚好为2,情况会比前面复杂点。我们可以简单分析:1~15200,百位出现 2 的次数为 1500次,15200~15292 百位出现 2 的次数为92+1 = 93 次,所以一共是 1593次。

接着是千位: 15去掉 5变成 1 ,所以在千位出现的次数是 1000,因为 5 > 2 ,所以应该再加上1000(因为前面有个、十、百位),所以有 2000 次。

接下来是 万位,因为万位是 1 ,小于2, 所以万位不可能出现 2 。

统计下来一共为 6653次。

接下来需要将上述分析过程转化为 代码

//根据数字规律,时间复杂度为 logn 的算法
	public static int numOfN(int n,int x){
		int count = 0;
		int k = 1;
		int temp = n;
		for(int i = 1; i <= n; i *= 10){
			k = temp / 10;
			count += k*i;
			int p = temp%10;
			if(p > x )
				count = count + i;
			if(p == x ){
				if(i > 1){
					int b = n%i;
					count += b + 1;
				}
				if(i == 1)
					count++;
			}
			temp = k; 			
		}	
		return count;
	}


综合以上联众方法进行实际测试:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值