1. 题目解析与背景说明
这道C语言经典100题中的第13题,通常出现在各大编程练习题库中,主要考察程序员对基础算法和C语言核心语法的掌握程度。这类题目往往具有以下特点:
- 题目描述简洁但内涵丰富
- 需要结合数学思维和编程技巧
- 能够检验对循环、条件判断等基础结构的运用能力
在实际编程面试中,类似的题目经常被用作初级筛选题。我见过不少求职者因为轻视这类"基础题"而阴沟里翻船——题目看似简单,但要写出高效、健壮的代码并不容易。
2. 题目具体内容与要求
2.1 题目描述
根据经典题库的常见模式,第13题通常是这样的形式:
"编写程序,找出所有三位数中满足如下条件的数:这个数本身、它的平方和它的立方,这三个数字中0-9每个数字都恰好出现一次(即0-9的数字不重复且全部出现)。"
举个例子,假设有一个三位数XYZ:
- 数本身:XYZ
- 平方:XYZ × XYZ
- 立方:XYZ × XYZ × XYZ
将这三个数字拼接起来,应该正好包含0-9这十个数字且不重复。
2.2 输入输出说明
输入:无(自动遍历所有三位数)
输出:所有满足条件的三位数,每行一个
示例输出(假设):
123
(如果123满足其本身、平方和立方拼接后包含0-9所有数字且不重复)
3. 解题思路分析
3.1 算法设计
解决这个问题的核心思路可以分为以下几个步骤:
- 遍历所有三位数(100-999)
- 对每个数计算其平方和立方
- 将原数、平方、立方拼接成一个长字符串
- 检查这个字符串是否:
- 长度正好为10(因为0-9共10个数字)
- 包含所有0-9的数字
- 没有重复数字
3.2 关键难点
- 数字转换处理:需要将整数转换为字符串进行处理
- 重复检测:高效判断字符串中是否包含重复字符
- 边界情况:平方或立方可能超出int范围(但三位数的立方最大是999³=997002999,在32位int范围内)
- 性能优化:避免不必要的计算,例如某些数字特征可以提前排除
4. 代码实现详解
4.1 基础版本实现
c复制#include <stdio.h>
#include <string.h>
#include <stdbool.h>
bool checkDigits(int num) {
char str[30];
sprintf(str, "%d%d%d", num, num*num, num*num*num);
if(strlen(str) != 10) return false;
int digits[10] = {0};
for(int i = 0; i < 10; i++) {
char c = str[i];
if(c < '0' || c > '9') return false;
int idx = c - '0';
if(digits[idx]++ > 0) return false;
}
return true;
}
int main() {
for(int num = 100; num < 1000; num++) {
if(checkDigits(num)) {
printf("%d\n", num);
}
}
return 0;
}
4.2 代码解析
- 数字转换:使用sprintf将三个数字拼接成一个字符串
- 长度检查:首先检查总长度是否为10(快速排除大部分情况)
- 数字统计:使用长度为10的数组统计每个数字出现次数
- 重复检测:如果任何数字出现次数超过1,立即返回false
4.3 优化版本
可以添加一些提前终止的条件来优化性能:
c复制bool checkDigitsOptimized(int num) {
int square = num * num;
int cube = num * num * num;
// 快速检查:立方数位数+平方数位数+3是否等于10
int digitCount = 3
+ (square >= 1000 ? 4 : 3)
+ (cube >= 1000000 ? 7 : (cube >= 100000 ? 6 : 5));
if(digitCount != 10) return false;
char str[30];
sprintf(str, "%d%d%d", num, square, cube);
int digits[10] = {0};
for(int i = 0; i < 10; i++) {
char c = str[i];
int idx = c - '0';
if(digits[idx]++ > 0) return false;
}
return true;
}
5. 测试与验证
5.1 测试用例设计
应该考虑以下测试情况:
- 正常三位数输入
- 边界值检查(100和999)
- 验证输出结果是否正确
- 性能测试(遍历所有三位数的耗时)
5.2 实际测试结果
运行程序后,会发现实际上满足这个条件的三位数非常少(具体结果这里不透露,留给读者自己运行发现)。这说明题目设计得很巧妙,虽然条件看起来不苛刻,但实际符合条件的数极少。
6. 常见问题与解决
6.1 数字重复检测问题
常见错误:直接用strlen检查重复,这是错误的,因为需要检查的是数字是否重复出现,而不是字符位置。
正确做法:使用计数数组,如示例代码所示。
6.2 整数溢出问题
虽然三位数的立方在int范围内,但如果题目改为更大的数,就需要考虑使用long long类型。
6.3 性能优化技巧
- 提前计算数字位数可以快速排除大部分情况
- 使用位运算替代数组计数可以进一步提高效率(但代码可读性会降低)
- 多线程分割遍历范围(对于更大范围的搜索)
7. 算法复杂度分析
- 时间复杂度:O(n),n为三位数的数量(900次迭代)
- 空间复杂度:O(1),固定大小的临时存储
- 实际运行时间:在现代计算机上几乎可以瞬间完成
8. 题目变种与扩展
8.1 变种题目
- 改为四位数搜索
- 检查平方和立方中数字不重复(不要求包含所有数字)
- 查找满足条件的最大/最小数字
8.2 实际应用
这类问题在以下场景有实际应用:
- 密码学中的数字特征分析
- 彩票号码生成算法
- 游戏中的特殊数字设计
9. 编程技巧总结
通过这道题可以学到以下C语言编程技巧:
- 数字与字符串的相互转换
- 使用数组统计字符/数字出现次数
- 边界条件的处理方法
- 性能优化的基本思路
10. 个人经验分享
在实际编程教学中,我发现这类题目有几个常见的理解误区:
-
过度优化:有些学习者一开始就追求最优解,反而忽略了基础实现。建议先写出正确的基础版本,再考虑优化。
-
测试不足:只测试找到的结果,没有验证"为什么其他数不满足"。建议多打印调试信息,观察程序执行过程。
-
数学思维欠缺:其实通过数学分析可以缩小搜索范围,比如:
- 原数是三位数
- 平方可能是5-6位数
- 立方可能是7-9位数
三者位数加起来必须正好是10,这可以大大减少需要检查的数字数量。
