C语言学习笔记20260624-统计数字出现次数(暴力枚举与数位统计法)
一、学习目标
掌握计算机解决"区间数字统计"类问题的两种核心算法思想。通过经典的"统计1到n中数字x出现次数"问题,深入理解暴力枚举法的底层逻辑,并学习如何通过数位统计公式法进行算法优化,体会从"逐个检查"到"按位贡献"的思维跃迁。
二、问题拆解与核心逻辑
本题要求计算在区间 1, n 的所有整数中,数字 x(0 ≤ x ≤ 9)共出现了多少次。核心约束条件为:
- 区间约束:遍历范围是从 1 到 n。
- 统计目标:不仅要统计包含 x 的数字个数,还要统计 x 出现的总次数(例如数字 22 中包含两个 2,应计数 2 次)。
三、方法一:暴力枚举法(逐位检查)
3.1 核心思路
利用 for 循环遍历 1 到 n 的每一个整数。对于每一个整数,通过"取余"和"整除"操作,将其每一位数字拆解出来,逐一与目标数字 x 进行比对。
3.2 代码实现
c
#include <stdio.h>
int main()
{
int n, x;
scanf("%d %d", &n, &x);
int count = 0;
int j = 0;
for (int i = 1; i <= n; i++)
{
j = i; // 使用临时变量j,避免破坏循环变量i
while (j) // 只要j不为0,就继续拆解它的每一位
{
if (j % 10 == x) // 取出当前最低位,判断是否等于x
count++;
j /= 10; // 去掉最低位,将下一位拉到最低位
}
}
printf("%d", count);
return 0;
}
3.3 方法优缺点分析
- 优点:逻辑极其直观,代码编写简单,不需要复杂的数学推导,是验证小规模数据答案的首选。
- 缺点:时间复杂度为 O(n × log n)。当 n 达到百万级(10^6)时,运算次数接近千万级,尚可接受;但当 n 达到亿级(10^8)甚至更高时,暴力法会严重超时。
四、方法二:数位统计公式法(数学优化)
4.1 核心思想:按位贡献法
不逐个枚举数字,而是将问题转化为"逐位计算贡献"。对于数字 n 的任意一位(个位、十位、百位......),该位上数字 x 出现的次数,只与**高位(high)、当前位(cur)、低位(low)**三部分有关。时间复杂度仅为 O(log n)。
4.2 代码实现与分类讨论
c
#include <stdio.h>
// 计算1~n中数字x出现总次数
long long countX(long long n, int x)
{
long long res = 0;
long long base = 1; // 位权:1、10、100... 充当"探针"
long long high, cur, low;
while (base <= n)
{
high = n / (base * 10); // 高位部分
cur = (n / base) % 10; // 当前位数字
low = n % base; // 低位部分
if (x == 0)
{
// x为0时,高位不能取0(不能有前导零),单独修正公式
if (high == 0) // 当前位是最高位,0不能作为最高位,直接跳过
{
base *= 10;
continue;
}
if (cur > x)
res += (high - 1) * base + base;
else if (cur == x)
res += (high - 1) * base + low + 1;
else
res += (high - 1) * base;
}
else
{
// x 1~9通用公式
if (cur < x)
res += high * base;
else if (cur == x)
res += high * base + low + 1;
else
res += (high + 1) * base;
}
base *= 10; // 探针右移一位,处理下一位
}
return res;
}
int main()
{
int n, x;
scanf("%d%d", &n, &x);
printf("%lld\n", countX(n, x));
return 0;
}
4.3 核心细节解析
- base 的作用 :
base就像一个探针,它的值是多少(1, 10, 100...),就代表当前正在处理哪一位。通过n / base和n % base就能把 n 按当前位"切开"。 - x=0 的特殊处理:这是最容易出错的地方。数字的最高位不能为 0(即不能有前导零)。例如在统计百位上的 0 时,高位不能从 0 开始取(如 000xx 实际上是两位数),必须从 1 开始取,因此公式中高位部分需要减去 1。
- 分类讨论的精髓 :根据当前位
cur与目标数字x的大小关系(小于、等于、大于),推导出不同的数学公式,直接计算该位上 x 出现的总次数。
五、总结与工程实践建议
暴力枚举法是理解计算机穷举思维的基石,其"取余拆解每一位"的技巧在各类算法题中极为通用。但在面对大区间数字统计问题时,数位统计公式法是更优的工程实践。它不仅将时间复杂度从 O(n × log n) 降低到了 O(log n),更展示了"将全局问题分解为局部贡献"这一解决复杂数学问题的通用范式。在实际开发中,当 n 的范围较小时,可采用暴力法快速解题;当 n 达到 10^8 甚至 10^18 级别时,必须采用数位统计法。