找出两个数组中的重复元素
给定两个很大的正整数数组,元素大小均小于1,000,000,找出所有在两个数组中都出现过得数字。
看到这个问题,一个很直接的问题就是使用一个set,将一个数组中的数字全部放入set中。再去遍历另一个数组中的元素,查看是否在set中即可。
但是题目给定的数组都很大,采用set可能会超出内存的限制。那么有无可能缩小内存使用限制呢?是否有必要存放int类型来作为标识呢?
其实可以采用位图法,创建一个长度为1,000,000的bit数组,然后遍历第一个数组,将出现的数字对应的下标修改为1。然后再遍历第二个数组,如果bit数组数字对应的小标的值为1,则代表是重复数字。因为bit占1bit,但是一个int是4bytes = 32bits。参考代码如下:
class BitMap {
private byte[] bits;
public BitMap(int size) {
this.bits = new byte[size];
}
public boolean contains(int value) {
int index = value / 8;
int offset = value % 8;
byte mask = (byte) (1 << offset);
return (bits[index] & mask) != 0;
}
public void add(int value) {
int index = value / 8;
int offset = value % 8;
byte mask = (byte) (1 << offset);
bits[index] |= mask;
}
}
BitMap bitMap = new BitMap(1000000);
for (int i : arr1){
bitMap.add(arr1);
}
for (int i : arr2){
if (bitMap.contains(i))
System.out.println(i);
}
但如果是单个数组也无法放入内存中的情况呢?这时候就需要分而治之了。我们只需要将数组1和数组2按照相同逻辑进行拆分,确保相同的数字会分到一个分区中。例如数组1中的6分到了第一个分区,那么数组2中的6也应该分到第一个分区。之后我们只需要在对应的分区之间判断有哪些重复的数字,最后再合并结果即可。
其实这里还有一个数据倾斜的坑,我们留到下一篇讲排序的时候再谈。