从1到n整数中1出现的次数(Java)

本文介绍了一种高效算法来计算从1到给定整数N中数字1出现的总次数。通过分析数字构成规律,提出了一种优于简单遍历的方法,并给出了详细的解析及代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目:

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

第一思路:

利用for循环累加从1到n中每个整数出现1的次数。判断每个整数是否出现1,利用对整数每次对10求余,判断整数的个位数字是不是1。如果这个数字大于10,除以10之后再判断个位数字是不是1。

代码实现:

/**
 * 求1到n中的每个整数中1出现的总共有多少次
 */
public static int numberOf1Between1AndN(int n){
	if(n<0){			
		return 0;
	}
	int count = 0;
		
	for (int i = 1; i <= n; i++) {
		count += numberOf1(i);
	}
	return count;
}
/**
 * 求每个整数中1出现的次数
 * @param n
 * @return
 */
private static int numberOf1(int n) {
	int count = 0;
	while(n>0){
		if(n % 10 == 1){
			++count;
		}
		n = n / 10;
	}
	return count;
}

最优思路:从数字规律着手,提高时间效率

1:如果第i位(从低位向高位开始)上的数字是0,那么第i位可能出现1的次数仅由更高位决定(如果没有高位,则视高位为0),等于更高位数字*当前位数的权重10^(i-1);

2:如果第i位上的数字为1,则第i位上可能出现1的次数不仅受更高位影响还受低位影响(如果没有低位,则视低位为0),等于更高位数字*当前位数的权重10^(i-1) + (低位数字+1);

3:如果第i位上的数字大于1,则第i位上可能出现1的次数仅由更高位决定(如果没有高位,则视高位为0),等于(更高位数字+1)*当前位数的权重10^(i-1)。

注:(这里的 X∈[1,9] ,因为 X=0 不符合下列规律,需要单独计算)。

首先要知道以下的规律: 

从 1 至 10,在它们的个位数中,任意的 X 都出现了 1 次。 

从 1 至 100,在它们的十位数中,任意的 X 都出现了 10 次。 

从 1 至 1000,在它们的百位数中,任意的 X 都出现了 100 次。

依此类推,从 1 至 10^ i ,在它们的左数第二位(右数第 i 位)中,任意的 X 都出现了 10^(i-1) 次。

以21354为例,寻找1出现的次数:

个位:从1至21350中包含了2135个10,因此1出现了2135次,21351,21352,21353,21354其中21351包含了一个1,故个位出现1的次数为:2135*10(1-1) + 1 = 2136次;

公式:( 2135+1)* 10^(1-1) = 2136;

十位:从1到21300中包含了213个100,因此1出现了213 * 10^(2-1) = 2130次,剩下的数字是21301到21354,它们的十位数字是5 > 1;因此它会包含10个1;故总数为2130 + 10 = 2140次;

公式:(213 + 1)* 10^(2-1) = 2140次;

百位:从1到21000中包含了21个1000,因此1出现了21 * 10^(3-1) = 2100次,剩下的数字是21001到21354,它们的百位数字是3 > 1;因此它会包含100个1;故总数为2100 + 100 = 2200次;

公式:(21 + 1)* 10^(3-1) = 2200次;

千位:从1到20000中包含了2个10000,因此1出现了2 * 10^(4-1) = 2000次,剩下的数字是20001到21354,它们的千位数字是1 = 1;情况稍微复杂些,354 + 1 = 355;故1的总数为2000 + 355 = 2355次;

公式:2 * 10^(4-1) + 354 + 1 = 2355次;

万位:万位是2 > 1,没有更高位;因此1出现的次数是1 * 10^(5-1) = 10000次;

公式:(0 + 1)*10^(5-1) = 10000次;

故总共为:2136+2140+2200+2355+10000=18831次;

故总结:

  • 1、取第 i 位左边的数字(高位),乘以 10 ^(i−1) ,得到基础值 a 。
  • 2、取第 i 位数字,计算修正值: 
    • 1、如果大于 X,则结果为 a+ 10 ^(i−1) 。
    • 2、如果小于 X,则结果为 a 。
    • 3、如果等 X,则取第 i 位右边(低位)数字,设为 b ,最后结果为 a+b+1 。

故代码实现:

public class Main1 {

	public int numberOf1Between1AndN(int n, int x){
		if(n < 0 || x < 1 || x > 9){
			return 0;
		}
		int curr, low, temp, high = n, i = 1, total = 0;
		while(high!=0){
			high = n / (int)Math.pow(10, i); //获取第i位的高位
			temp = n % (int)Math.pow(10, i); //
			curr = temp / (int)Math.pow(10, i-1); //获取第i位
			low = temp%(int)Math.pow(10, i-1); //获取第i位的低位
			if(curr == x){ //等于x
				total += high*(int)Math.pow(10, i-1)+ low + 1;
			}else if(curr < x){ //小于x
				total += high*(int) Math.pow(10, i-1);
			}else{ //大于x
				total += (high + 1) * (int)Math.pow(10, i-1);
			}
			i++;
		}
		return total;
	}
	
	public static void main(String[] args) {
		Main1 m1 = new Main1();
		int nums = m1.numberOf1Between1AndN(21354, 1);
		System.out.println(nums);
	}
}

小结:优中选优是拿下offer的王道。

### 统计指定数字在所有整数中出现的频率 要统计某个特定数字在整个整数范围内的出现频率,可以通过以下方式实现。假设目标是统计某一位数字 `d`(0 ≤ d ≤ 9)在给定的一组整数中的总出现次数。 #### 方法描述 对于每一个整数,将其分解成各位数字并逐一检查是否等于目标数字 `d`。如果是,则累加计数器。这种方法的时间复杂度主要取决于输入整数的数量以及它们的位数。具体来说,处理每一位数字的操作时间复杂度为 \(O(\log_{10} n)\)[^1],其中 \(n\) 是当前整数的最大值。 以下是基于 Python 实现的一个简单例子: ```python def count_digit_occurrences(numbers, target_digit): """ 计算目标数字在一组整数中出现的总次数。 参数: numbers (list): 整数列表。 target_digit (int): 要统计的目标数字(0-9)。 返回: int: 目标数字出现的总次数。 """ count = 0 for number in numbers: current_number = abs(number) # 处理负数情况 if current_number == 0 and target_digit == 0: count += 1 while current_number > 0: digit = current_number % 10 if digit == target_digit: count += 1 current_number //= 10 return count # 测试代码 numbers = [234, 567, 890, 123, 456, 789, 111, 222, 333] target_digit = 2 result = count_digit_occurrences(numbers, target_digit) print(f"数字 {target_digit} 在整数集合中出现了 {result} 次") ``` 上述代码通过逐一遍历每个整数并将它拆分为单独的数字来完成任务。当发现匹配的目标数字时,更新全局计数器。 --- #### 使用 MATLAB 进行统计 另一种方法是在 MATLAB 中使用内置函数 `tabulate()` 对数组中的元素进行频次统计[^2]。然而,该方法仅适用于直接统计整个数组中不同值的分布情况,而无法针对某一特定数字跨多位进行统计。因此需稍作修改以适应需求。 ```matlab function occurrences = countDigitInMatlab(numbers, targetDigit) occurrences = 0; for i = 1:length(numbers) numStr = num2str(abs(numbers(i))); % 将数字转换为字符串表示形式 matches = strfind(numStr, num2str(targetDigit)); % 查找子串位置 occurrences = occurrences + length(matches); end end % 示例调用 numbers = [234, 567, 890, 123, 456, 789, 111, 222, 333]; targetDigit = 2; occurrences = countDigitInMatlab(numbers, targetDigit); fprintf('数字 %d 出现了 %d 次\n', targetDigit, occurrences); ``` --- #### 数据降维背景下的思考 如果面对的是大规模高维度数据集,并希望简化问题以便更好地统计某些特征的分布特性,可以考虑采用主成分分析(PCA)或其他类似的降维技术[^4]。尽管这并非解决本题的核心手段,但它提供了一种思路——即如何有效减少冗余信息的同时保留重要模式。 --- #### Java 支持度计算扩展 类似于关联规则挖掘中的支持度概念[^3],我们也可以定义一个“数字支持度”的衡量标准:即某个数字在整个样本集中占的比例。这种比例可以直接反映其普遍性和代表性程度。 ```java public class DigitFrequency { public static int countOccurrences(int[] array, int targetDigit) { int count = 0; for (int number : array) { String numString = Math.abs(number) + ""; // 数字转字符串 for (char c : numString.toCharArray()) { if ((c - '0') == targetDigit) { count++; } } } return count; } public static void main(String[] args) { int[] data = {234, 567, 890, 123, 456, 789, 111, 222, 333}; int targetDigit = 2; System.out.println("Target digit appears " + countOccurrences(data, targetDigit) + " times."); } } ``` 以上展示了多种编程语言下实现相同功能的不同途径。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值