C语言学习笔记20260620-数字筛选算法:排除4的倍数与含4数字

C语言学习笔记20260620-数字筛选算法:排除4的倍数与含4数字

一、学习目标

掌握在C语言中处理"数字特征筛选"类问题的多种编程思路,包括数学取模运算、函数封装思想以及字符串处理技巧。通过对比三种不同的实现方法,深入理解底层算法逻辑与代码可读性之间的权衡。

二、问题拆解与核心逻辑

本题要求输出 1 到 n 之间既不是 4 的倍数,也不包含数字 4 的整数。解决该问题需要处理两个独立的判断条件:

  1. 数学条件 :判断当前数字 i 是否能被 4 整除(i % 4 == 0)。
  2. 数位条件 :判断当前数字 i 的十进制表示中是否包含数字 4。

只要满足任意一个条件,该数字就需要被过滤掉。

三、方法一:临时变量拆数法(数学取模)

3.1 核心思路

利用数学中的"取模(%)"和"整除(/)"运算,从低位到高位逐位拆解数字,检查每一位是否等于 4。

3.2 代码解析

c 复制代码
for (int i = 1; i <= n; i++)
{
    if (i % 4 == 0) continue; // 提前过滤掉4的倍数
    int tmp = i;
    int has4 = 0; // 标记变量
    while (tmp > 0)
    {
        if (tmp % 10 == 4) // 取出当前最低位判断
        {
            has4 = 1;
            break; // 找到4立即跳出循环,提高效率
        }
        tmp /= 10; // 去掉最低位,准备判断下一位
    }
    if (has4 == 0)
        printf("%d\n", i);
}

3.3 关键细节与优化

  • 保护原始数据 :使用临时变量 tmp 来承接 i 的值进行拆解,避免直接修改循环变量 i 导致死循环或逻辑错误。
  • 循环终止条件while (tmp > 0) 是拆数法的核心。当 tmp 变为 0 时,说明所有位数都已检查完毕。如果错误地写成 tmp >= 0,当 tmp 为 0 时会陷入死循环。
  • 提前终止 :一旦发现某一位是 4,立即使用 break 跳出循环,无需继续检查剩余的高位。

四、方法二:封装判断函数(模块化思想)

4.1 核心思路

将"判断是否包含数字4"这一独立逻辑抽取为一个单独的函数 containFour,使主函数 main 的逻辑更加清晰。

4.2 代码解析

c 复制代码
// 判断x是否包含数字4,含4返回1,不含返回0
int containFour(int x)
{
    while (x > 0)
    {
        if (x % 10 == 4)
            return 1; // 找到4直接返回真
        x /= 10;
    }
    return 0; // 遍历完所有位都没找到4,返回假
}

// 主函数逻辑极其简洁
for (int i = 1; i <= n; i++)
{
    // 两个条件满足任意一个就跳过
    if (i % 4 == 0 || containFour(i))
        continue;
    printf("%d\n", i);
}

4.3 优势分析

  • 可读性强 :主循环中的 if 条件读起来就像自然语言(如果是4的倍数 或 包含4,则跳过)。
  • 复用性高:如果后续题目要求判断是否包含数字 7,只需修改或新增一个函数,无需改动主循环逻辑。
  • 逻辑解耦:将复杂的拆数逻辑隐藏在函数内部,降低了主函数的认知负担。

五、方法三:字符串处理法(类型转换)

5.1 核心思路

将数字转换为字符串,把"数位检查"问题转化为"字符查找"问题。利用 sprintf 进行数字到字符串的转换,再遍历字符串查找字符 '4'。

5.2 代码解析

c 复制代码
char buf[20]; // 存放数字转成的字符串
for (int i = 1; i <= n; i++)
{
    if (i % 4 == 0) continue;
    sprintf(buf, "%d", i); // 将整数 i 格式化输出到字符数组 buf 中
    int has4 = 0;
    for (int j = 0; buf[j] != '\0'; j++)
    {
        if (buf[j] == '4') // 注意这里比较的是字符 '4'
        {
            has4 = 1;
            break;
        }
    }
    if (!has4)
        printf("%d\n", i);
}

5.3 适用场景与局限

  • 适用场景:当判断条件变得复杂时(例如:包含连续两个4,或者包含4或7),字符串处理法往往比数学拆数法更直观,且不易出错。
  • 局限性:涉及类型转换和额外的字符数组内存分配,运行效率略低于纯数学运算。

在实际编程与算法竞赛中,方法2(封装函数)通常是最佳实践,它兼顾了代码的整洁度与执行效率。而方法1 是理解底层数位拆解的基础,方法3则在处理复杂数位规则时提供了另一种灵活的解题视角。