0954. Array of Doubled Pairs - kumaeki/LeetCode GitHub Wiki
Given an array of integers arr of even length, return true if and only if it is possible to reorder it such that arr[2 * i + 1] = 2 * arr[2 * i] for every 0 <= i < len(arr) / 2.
Example 1:
Input: arr = [3,1,3,6]
Output: false
Example 2:
Input: arr = [2,1,2,6]
Output: false
Example 3:
Input: arr = [4,-2,2,-4]
Output: true
Explanation: We can take two groups, [-2,-4] and [2,4] to form [-2,-4,2,4] or [2,4,-2,-4].
Example 4:
Input: arr = [1,2,4,16,8,4]
Output: false
Constraints:
- 0 <= arr.length <= 3 * 10^4
- arr.length is even.
- -10^5 <= arr[i] <= 10^5
解法1
把0, 正数和负数分开计算.
class Solution {
public boolean canReorderDoubled(int[] arr) {
Arrays.sort(arr);
Map<Integer, Integer> map = new HashMap<>();
for (int i : arr)
map.put(i, map.getOrDefault(i, 0) + 1);
if (map.getOrDefault(0, 0) % 2 != 0)
return false;
for (int i = getMinPositive(arr); i < arr.length; i++) {
int n1 = arr[i], n2 = arr[i] * 2;
// 如果n1计数为0, 那么说明已经被之前的计算用掉了
if (map.get(n1) == 0)
continue;
// 如果n2计数为0, 说明该数组不满足条件,返回false
if (map.getOrDefault(n2, 0) == 0)
return false;
// 完成当前计算, n1和n2计数减一
map.put(n1, map.get(n1) - 1);
map.put(n2, map.get(n2) - 1);
}
for (int j = getMaxNegative(arr); j >= 0; j--) {
int n1 = arr[j], n2 = arr[j] * 2;
// 如果n1计数为0, 那么说明已经被之前的计算用掉了
if (map.get(n1) == 0)
continue;
// 如果n2计数为0, 说明该数组不满足条件,返回false
if (map.getOrDefault(n2, 0) == 0)
return false;
// 完成当前计算, n1和n2计数减一
map.put(n1, map.get(n1) - 1);
map.put(n2, map.get(n2) - 1);
}
return true;
}
// 得到最小正数的下标
private int getMinPositive(int[] arr) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] <= 0)
left = mid + 1;
else
right = mid - 1;
}
return left;
}
// 得到最大负数的下标
private int getMaxNegative(int[] arr) {
int left = 0, right = arr.length - 1;
while (left <= right) {
int mid = (left + right) / 2;
if (arr[mid] < 0)
left = mid + 1;
else
right = mid - 1;
}
return left - 1;
}
}
解法2
用空间换时间.
因为最大值和最小值都只有10^5, 所以最坏情况下也只需要两个长度为10^5的数组.
class Solution {
public boolean canReorderDoubled(int[] A) {
int max = 0, min = 0;
for (int a : A) {
max = Math.max(max, a);
min = Math.min(min, a);
}
int[] neg = new int[-min + 1], pos = new int[max + 1];
int count0 = 0;
for (int a : A) {
if (a > 0)
pos[a]++;
else if (a < 0)
neg[-a]++;
else
count0++;
}
if (count0 % 2 == 1)
return false;
for (int i = 0, mid = max / 2; i <= max; i++)
if (pos[i] > 0)
if (i > mid || pos[i * 2] < pos[i])
return false;
else {
pos[i * 2] -= pos[i];
pos[i] = 0;
}
for (int i = 0, mid = -min / 2; i <= -min; i++)
if (neg[i] > 0)
if (i > mid || neg[i * 2] < neg[i])
return false;
else {
neg[i * 2] -= neg[i];
neg[i] = 0;
}
return true;
}
}
解法3
更简洁的解法. 基于解法1.
class Solution {
public boolean canReorderDoubled(int[] A) {
Map<Integer, Integer> map = new HashMap<Integer, Integer>();
for (int a : A)
map.put(a, map.getOrDefault(a, 0) + 1);
Arrays.sort(A);
for (int a : A)
// 如果当前值还没有被计算过
if (map.get(a) > 0) {
// 如果是小于0的奇数, 那么直接返回false
if (a < 0 && a % 2 == 1)
return false;
// 负数的排列是按照绝对值的降序排列, 所以先出现的是2*x, 然后才是x
// 而正数的排列是正常先x 然后是2*x
// 所以正数是乘以2, 负数是除以2
int t = a > 0 ? a * 2 : a / 2;
// 如果目标t的个数小于a的个数, 那么不可能得到结果, 返回false
if (map.getOrDefault(t, 0) < map.get(a))
return false;
// 更新目标t的个数(减去a的个数)
map.put(t, map.get(t) - map.get(a));
// 更新a的个数
map.put(a, 0);
}
return true;
}
}