题目描述
小C对“好数”非常感兴趣,她定义一个不含前导零的正整数为“好数”,如果它的所有数位最多包含两种不同的数字。例如,数字 23,2323,9,111,和 101 都是好数。现在小C想知道,从1到n之间有多少个好数。
例如:当n=110时,所有的1位数、2位数,以及一些3位数(如 100, 101)都是好数,一共有102个。
解决方案
为了找出从1到n之间的“好数”,我们需要遍历每个数字并检查它是否满足“好数”的定义。具体来说,每个数字的所有数位必须最多包含两种不同的数字。我们可以利用字符串处理和集合来高效地判断每个数字是否为“好数”。
思路
-
遍历所有数字:
- 从1到n遍历所有数字,并逐个检查每个数字是否为“好数”。
-
使用集合记录数位:
- 将每个数字转换为字符串,以便逐位处理其数位。
- 使用集合来记录数字的不同数位,因为集合可以自动去重且便于统计数位种类。
-
判断是否为“好数” :
- 如果一个数字的数位种类数大于2,则该数字不是“好数”。
- 统计1到n之间的“好数”数量。
时间复杂度和空间复杂度分析
- 时间复杂度:遍历从1到n的所有数字,每个数字的长度最多为log10(n)(即最多包含log10(n)位数),因此时间复杂度为O(n * log10(n))。
- 空间复杂度:使用集合来存储数位种类,集合的大小最多为10(因为数位范围是0到9),因此空间复杂度为O(1)。
代码实现
以下是完整的Java代码实现:
java
import java.util.HashSet;
import java.util.Set;
public class GoodNumbers {
public static void main(String[] args) {
int n = 110;
System.out.println("从 1 到 " + n + " 之间的好数数量是: " + countGoodNumbers(n));
}
public static int countGoodNumbers(int n) {
int count = 0;
for (int i = 1; i <= n; i++) {
if (isGoodNumber(i)) {
count++;
}
}
return count;
}
private static boolean isGoodNumber(int num) {
String s = String.valueOf(num);
Set<Character> digits = new HashSet<>();
for (char c : s.toCharArray()) {
digits.add(c);
if (digits.size() > 2) {
return false;
}
}
return true;
}
}
代码解释
-
主函数:
main方法定义了n的值并调用countGoodNumbers方法,输出从1到n之间的“好数”数量。例子中,n = 110,预期输出结果为102。
-
计算“好数”的数量:
countGoodNumbers方法接收一个整数n,初始化计数器count为0。- 遍历从1到n的所有数字,对于每个数字调用
isGoodNumber方法判断其是否为“好数”。 - 如果是“好数”,则计数器
count增加1。
-
判断是否为“好数” :
isGoodNumber方法接收一个整数num,将其转换为字符串s,方便逐位处理数位。- 使用一个集合
digits来记录数字中出现的不同数位。 - 遍历字符串
s中的每个字符,将其加入集合digits中。 - 如果集合
digits中的数位个数超过2,则该数字不是“好数”,返回false。 - 否则,该数字是“好数”,返回
true。
示例解析
让我们看一个具体的例子:当 n = 110 时,程序应该输出102个“好数”。下面是程序的部分执行流程:
- 数字1到9都满足“好数”的定义,因为它们只有一个数位。
- 数字10到99中,如果包含两种或一种数位的数字,例如22,33,101等,也都是“好数”。
- 数字100到110中,数字101是“好数”,而数字100,102,103等都不是“好数”。
复杂度分析
- 时间复杂度:由于需要遍历从1到n的所有数字,并逐位检查每个数字的数位,因此时间复杂度为O(n * log10(n))。
- 空间复杂度:集合
digits的大小最多为10(因为数位范围是0到9),因此空间复杂度为O(1)。
总结
通过这一步步详尽的实现,我们可以高效地找到从1到n之间的所有“好数”。这个方法利用了字符串处理和集合操作,能够在合理的时间和空间复杂度内解决问题。