题目描述
小C对“好数”非常感兴趣,她定义一个不含前导零的正整数为“好数”,如果它的所有数位最多包含两种不同的数字。例如,数字 23,2323,9,111,和 101 都是好数。现在小C想知道,从1到 ( n ) 之间有多少个好数。
示例
- 输入:( n = 110 )
- 输出:102
- 输入:( n = 1000 )
- 输出:352
- 输入:( n = 1 )
- 输出:1
解题思路
分析需求
- 输入:一个正整数 ( n )。
- 输出:从1到 ( n ) 之间的好数的个数。
关键点
- 定义好数:一个正整数的所有数位最多包含两种不同的数字。
- 统计好数:从1到 ( n ) 之间,统计满足上述条件的数字个数。
算法步骤
- 初始化计数器:定义一个变量
cnt用于记录好数的个数。 - 处理特殊情况:
- 当 ( n \leq 101 ) 时,直接返回 ( n ),因为从1到101的所有数字都是好数。
- 当 ( n \leq 110 ) 时,直接返回102,因为从1到110的所有数字中有102个好数。
- 遍历范围:从111到 ( n ) 之间的每个数字,检查其是否为好数。
- 检查好数:
- 对于每个数字,统计其各个数位上不同数字的个数。
- 如果不同数字的个数不超过2,则该数字为好数。
- 返回结果:返回计数器
cnt的值。
代码解析
public class Main {
public static int solution(int n) {
// 特殊情况处理
if (n <= 101) return n;
if (n <= 110) return 102;
// 初始化计数器
int cnt = 102;
// 遍历从111到n之间的每个数字
for (int i = 111; i <= n; i++) {
if (isGoodNum(i)) cnt++;
}
return cnt;
}
public static boolean isGoodNum(int num) {
// 统计每个数字出现的次数
int[] cnt = new int[10];
while (num != 0) {
cnt[num % 10]++;
num /= 10;
}
// 统计不同数字的个数
int res = 0;
for (int i : cnt) {
if (i > 0) res++;
}
// 判断是否为好数
if (res <= 2) return true;
else return false;
}
public static void main(String[] args) {
System.out.println(solution(110) == 102);
System.out.println(solution(1000) == 352);
System.out.println(solution(1) == 1);
}
}
- 特殊情况处理:对于 ( n \leq 101 ) 和 ( n \leq 110 ) 的情况,直接返回已知的结果。
- 遍历范围:从111到 ( n ) 之间的每个数字,调用
isGoodNum方法检查是否为好数。 - 检查好数:通过数组
cnt统计每个数字出现的次数,然后统计不同数字的个数,如果不超过2,则该数字为好数。
学习心得
1. 理解问题的本质
在解决这个问题时,首先要理解“好数”的定义,即一个正整数的所有数位最多包含两种不同的数字。通过明确这一点,我们可以更容易地设计算法来解决问题。
2. 特殊情况的处理
处理特殊情况可以显著提高算法的效率。在这个问题中,我们通过提前返回已知的结果来避免不必要的计算。这种技巧在实际编程中非常有用,可以节省大量的时间和资源。
3. 数据结构的选择
选择合适的数据结构可以简化问题的解决过程。在这个问题中,我们使用了一个长度为10的数组来统计每个数字出现的次数。这种做法不仅简单明了,而且效率高。
4. 循环和条件判断的结合
循环和条件判断是编程中非常常见的操作。在这个问题中,我们使用了多个循环和条件判断来统计好数。合理地结合循环和条件判断,可以使代码更加简洁和高效。
5. 测试用例的重要性
编写测试用例可以帮助我们验证算法的正确性。在这个问题中,我们提供了几个典型的测试用例,确保我们的算法能够处理各种边界情况。通过这些测试用例,我们可以及时发现和修复潜在的问题。
总结
通过解决这个问题,我深刻体会到了算法设计的重要性。合理的算法设计不仅可以提高程序的效率,还可以使代码更加清晰和易于维护。同时,我也学会了如何通过处理特殊情况和选择合适的数据结构来优化算法,这对于解决复杂问题非常有帮助。在未来的学习和工作中,我会继续努力提升自己的算法能力,以应对更多挑战。