1. 检查数组对是否可以被 k 整除
(1)双指针(暴力超时)
把数组中的元素每两个分为一组,总共分为n/2组,然后确保每组都能被k整除,这样结果才会返回true,否则返回false。
每个元素都必须有另外一个元素与他相匹配才行,只要有一个不匹配就返回false
- 其实这里面有个数学问题,假如有两组数据(a,b)和(c,d)他们都能被k整除,也就是说(a+b)%k=0,并且(c+d)%k=0;如果(a+c)%k=0,那么(b+d)%k=0肯定也是成立的。(这里的所有字母都是整数)
- 举个例子,比如(3,5),(7,9)都能被4整除,如果(3+9)能被4整除,那么(5+7)也一定能被4整除。
- 所以我们很容易想到暴力求解,我们使用 两个指针,一个指针指向一个固定的元素,另一个指针从这个固定的元素下一个开始查找,如果找到就把这两个元素标记为删除,然后再继续查找……。如果没找到就直接返回false,我们以示例2为例来画个图看一下
class Solution {
public boolean canArrange(int[] arr, int k) {
int length = arr.length;
boolean[] visit = new boolean[arr.length];
for (int i = 0; i < length - 1; i++) {
if (visit[i])//数字被访问过了,就不能再用了
continue;
for (int j = i + 1; j < length; j++) {
if (visit[j])//数字被访问过了,就不能再用了
continue;
if ((arr[i] + arr[j]) % k == 0) {
//如果被找到了,我们就把他标记为已使用,
//下次就不会再用它了
visit[i] = visit[j] = true;
break;
}
}
if (!visit[i])//没找到匹配的直接返回false
return false;
}
return true;
}
}
(2)Map(优化)
- 我们知道,如果a+b能被k整除,那么a和b分别对k求余的结果相加也一定能被k整除,即(a%k+b%k)%k=0。所以我们可以对上面数组中的元素分别对k求余。
- 即num=num%k,因为数组中可能会有负数,所以求余的结果也可能为负,这里为了计算方便,我们把求余的结果全部转化为非负数,大小在[0,k-1]中,包含0和k-1。所以计算公式是num=(num%k+k)%k
- 我们只需要计算余数相对应位置上的个数是否相等就可以了,举个例子,比如k是5,那么余数中1的个数必须和4的个数一样多,2的个数必须和3的个数一样多,这样才能匹配成功,否则直接返回false。还有一点是0的个数必须是偶数
- 比如余数中[1,2,1,3,4,1]由于2和3的个数都是1所以能组合成一组,但1的个数和4的个数不一致,所以只有一个能组合成功,另一对组合失败。最后我们再来看下代码
class Solution {
public boolean canArrange(int[] arr, int k) {
Map<Integer, Integer> map = new HashMap<>();
//存储余数到Map
for(int i = 0; i < arr.length; i ++){
int mod = ((arr[i]%k)+k)%k; //保证余数不为负数
map.put(mod, map.getOrDefault(mod, 0) + 1);
}
//检验余数为零元素的数量,奇数返回false
if(map.getOrDefault(0, 0) % 2 == 1){
return false;
}
for(Integer key : map.keySet()){
//余数0已经检验
if(key == 0){
continue;
}
//没有对应余数的键 或者 对应余数键的值与此键(key)的值不相等
if(!map.containsKey((k-key)) || !map.get((k - key)).equals(map.get(key))){
return false;
}
}
return true;
}
}
(3)数组优化(更容易理解)
class Solution {
public boolean canArrange(int[] arr, int k) {
//mod数组存储余数结果
//余数取不到k,最大k-1;
int[] mod = new int[k];
//存储余数到Mod数组中
for (int num : arr) {
mod[(num % k + k) % k]++;
}
//如果余数为0的个数为奇数
if (mod[0] % 2 != 0) {
return false;
}
//最后比较余i和余k-i的数量是否相同,int i = 1; 2*i < k; i++即可
for (int i = 1; 2 * i < k; i++) {
if (mod[i] != mod[k-i]) {
return false;
}
}
return true;
}
}