在这篇博客中,我们将探讨一道有趣的算法题:如何从一个数组中找到两个数的乘积为“完美数”的配对,并计算这样的配对数目。通过问题描述、思路剖析、代码实现及优化探讨,让你对这类题目有更加深入的理解。
问题描述
完美数的定义:一个数被称为“完美数”,当且仅当它的数位中只有一个非零数字。例如:
- 是完美数:
5000,4,1,10,200; - 不是完美数:
25,123,303。
题目要求:给定一个大小为 n 的数组,从中选择两个元素,使它们的乘积是一个“完美数”。求这样的配对总数。
示例
样例 1
输入:arr = [25, 2, 1, 16]
输出:3
说明:三个配对分别是 (25, 2), (25, 1), (2, 1)。
样例 2
输入:arr = [5, 50, 500, 5000]
输出:0
说明:数组中没有满足条件的配对。
样例 3
输入:arr = [2, 10, 100, 1000]
输出:6
说明:六个配对分别是 (2, 10), (2, 100), (2, 1000), (10, 100), (10, 1000), (100, 1000)。
思路详解
1. 乘积是完美数的判定方法
要判断两个数的乘积是否是完美数,需要以下步骤:
- 计算乘积
product = a * b。 - 统计
product中的非零数字数量nonZeroCount。 - 若
nonZeroCount == 1,则该乘积为完美数。
2. 暴力法求解
最直观的方法是对数组中的每一对数字 (i, j) 检查是否满足条件。遍历数组的所有组合,复杂度为 (O(n^2))。
代码实现如下:
public static int solution(int[] arr) {
int count = 0;
for (int i = 0; i < arr.length; i++) {
for (int j = i + 1; j < arr.length; j++) {
if (isPerfectProduct(arr[i], arr[j])) {
count++;
}
}
}
return count;
}
public static boolean isPerfectProduct(int a, int b) {
int product = a * b;
int nonZeroCount = 0;
while (product > 0) {
if (product % 10 != 0) {
nonZeroCount++;
}
product /= 10;
}
return nonZeroCount == 1;
}
图解思路
以样例 arr = [25, 2, 1, 16] 为例,画出配对过程:
- 初始数组:
[25, 2, 1, 16] - 配对结果:
(25, 2):乘积为50,是完美数。(25, 1):乘积为25,不是完美数。(25, 16):乘积为400,是完美数。(2, 1):乘积为2,是完美数。(2, 16):乘积为32,不是完美数。(1, 16):乘积为16,是完美数。
最终共有 3 种有效配对。
时间复杂度分析
- 外层双重循环的复杂度为 (O(n^2))。
- 判断一个数是否为完美数的复杂度为 (O(\log_{10}k)),其中 (k) 是乘积的大小。
总时间复杂度约为 (O(n^2 \cdot \log_{10}k))。
优化思路
考虑到暴力解法的时间复杂度较高,我们可以尝试以下优化:
- 预处理数据:将所有数字转化为以其第一个非零数字为标识的形式。
- 例如:
5000 -> 5,100 -> 1。
- 例如:
- 利用字典存储信息:统计每个“关键数字”的出现次数,减少不必要的计算。
通过优化,可显著减少重复计算的开销,提升效率。
总结
这道题通过定义特殊数字及其相关性,考察了组合计算和逻辑判定的能力。我们采用了暴力法实现并给出了优化思路。关键在于对问题特性的深刻理解,结合数据预处理提升算法效率。
代码完整性和优化,是这类问题解法的核心。你也可以尝试实现上述优化,进一步挑战更大的数据规模!