数组的最大公因数排序
难度:
标签:
题目描述
You are given an integer array nums
, and you can perform the following operation any number of times on nums
:
- Swap the positions of two elements
nums[i]
andnums[j]
ifgcd(nums[i], nums[j]) > 1
wheregcd(nums[i], nums[j])
is the greatest common divisor ofnums[i]
andnums[j]
.
Return true
if it is possible to sort nums
in non-decreasing order using the above swap method, or false
otherwise.
Example 1:
Input: nums = [7,21,3] Output: true Explanation: We can sort [7,21,3] by performing the following operations: - Swap 7 and 21 because gcd(7,21) = 7. nums = [21,7,3] - Swap 21 and 3 because gcd(21,3) = 3. nums = [3,7,21]
Example 2:
Input: nums = [5,2,6,2] Output: false Explanation: It is impossible to sort the array because 5 cannot be swapped with any other element.
Example 3:
Input: nums = [10,5,9,3,15] Output: true We can sort [10,5,9,3,15] by performing the following operations: - Swap 10 and 15 because gcd(10,15) = 5. nums = [15,5,9,3,10] - Swap 15 and 3 because gcd(15,3) = 3. nums = [3,5,9,15,10] - Swap 10 and 15 because gcd(10,15) = 5. nums = [3,5,9,10,15]
Constraints:
1 <= nums.length <= 3 * 104
2 <= nums[i] <= 105
代码结果
运行时间: 494 ms, 内存: 55.5 MB
/*
* Solution approach using Java Streams:
* 1. Create a sorted copy of the array, sortedNums.
* 2. Identify the components of connected elements using Union-Find by calculating gcd for each pair.
* 3. Use Stream operations to handle arrays and collections.
*/
import java.util.*;
import java.util.stream.*;
public class SolutionStream {
private int gcd(int a, int b) {
while (b != 0) {
int temp = b;
b = a % b;
a = temp;
}
return a;
}
public boolean canSortByGCD(int[] nums) {
int[] sortedNums = Arrays.stream(nums).sorted().toArray();
UnionFind uf = new UnionFind(100001);
IntStream.range(0, nums.length).forEach(i ->
IntStream.range(i + 1, nums.length).forEach(j -> {
if (gcd(nums[i], nums[j]) > 1) {
uf.union(nums[i], nums[j]);
}
})
);
return IntStream.range(0, nums.length).allMatch(i ->
uf.find(nums[i]) == uf.find(sortedNums[i])
);
}
class UnionFind {
private int[] parent;
public UnionFind(int size) {
parent = new int[size];
Arrays.setAll(parent, i -> i);
}
public int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
public void union(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootY] = rootX;
}
}
}
}
解释
方法:
本题解通过构建一个联合查找数据结构来解决问题。主要步骤包括初始化联合查找数组,筛选出所有的素数并将数组中的数与其最小的素因子进行连接。首先,定义每个数自身为其父节点。接着,通过埃拉托斯特尼筛法找出所有小于最大数+1的素数,并对于每个素数,遍历其倍数,如果倍数在数组中,将这个倍数与其最小素因子连接起来,形成一个联合查找组。最后,将原数组排序并比较原数组的每个元素与排序后数组的每个元素是否属于同一个联合查找组。如果所有元素都属于同一个组,说明可以通过交换使数组有序。
时间复杂度:
O(m log m + n log log n)
空间复杂度:
O(n + m)
代码细节讲解
🦆
在题解中,为何首先选择使用联合查找数据结构来解决这个问题?
▷🦆
如何确保通过埃拉托斯特尼筛法筛选出的素数是`nums`数组中的数的有效因子?
▷🦆
在联合查找数据结构中,`union`函数是如何确保只连接数组`nums`中实际存在的数的素因子?
▷