此篇之前,我们所学习的普通数组、链表、Hash、树、字符串等结构,递归、排序等思想,以及后面需要学习的滑动窗口、回溯、贪心、动态规划等思想,在面对超大规模数据场景时表现差强人意,亟需面对超大规模数据场景的解题思想,帮助我们以较小的代价快速解决该类问题。
总结三种经典解题思路:
方法一:使用位存储,例如:给定一个数组,其数据范围在1-32000,数据流远远不断到来,使用int类型的数组标记这些元素,需要32000个int块,所占用的内存大小就是128KB,但是如果使用最小单位Bit来标记这些元素,仅仅需要32000个Bit,所占用的内存大小就是4000B,也就是1000个int块。试想32000个int块和1000个int块熟大熟小呢?
方法二:使用外部排序,如果文件太大,无法在内存中放下,需要考虑将大文件分成若干小块,先处理每个块,然后再逐步得想要的结果。
方法三:使用堆结构,在超大数据流中找第K大、第K小、K个最大、K个最小,则特别适合使用堆来完成。
1.用4KB内存寻找重复元素
先针对方法一进行练习,分析如何使用位存储。
题目描述:给定一个数组,其数据范围在1-32000,数据流远远不断到来,只使用4KB内存,寻找数据流中重复元素。
题目分析:使用int类型的数组标记这些元素,需要32000个int块,所占用的内存大小就是128KB,但是如果使用最小单位Bit来标记这些元素,仅仅需要32000个Bit,所占用的内存大小就是4000B<4096B=4KB,也就是1000个int块。另一个关键点在于,如何确定数据流中的元素应该存储在哪一个int块,以及int块中的哪一个Bit?答:使用两个变量wordNumber和bitNumber维护元素应放置在哪一个int块和哪一位Bit。具体而言,1-32存放在第一个int块,33-64存放在第二个int块依次....;1存放在第1个Bit,33存放在第1个Bit,34存放在第二个Bit,Bit的确定就是:元素&0x1F(也就是31)
存放:当元素确定存放位置后,该位置的比特为0,则可以存放,每次存放应使用“或”运算,不能修改其他已经存放元素的位置。
取出:当元素确定存放位置后,该位置的比特为1,则元素重复,输出重复元素即可!
厘清思路,直接上代码!
public class FindDuplicatesIn32000 {
public static void main(String[] args) {
int[] array = {0,1,4,6,6,7,8,6,4,2,1,6};
checkDuplicates(array);
}
public static void checkDuplicates(int[] array) {
BitSet bs = new BitSet(32000);
for (int i = 0; i < array.length; i++) {
int num = array[i];
int num0 = num - 1;
if (bs.get(num)) {
System.out.println(num);
} else {
bs.set(num);
}
}
}
static class BitSet {
int[] bitset;
public BitSet(int size) {
this.bitset = new int[size >> 5];
}
boolean get(int pos) {
int wordNumber = (pos >> 5);//除以32
int bitNumber = (pos & 0x1F);//除以32
return (bitset[wordNumber] & (1 << bitNumber)) != 0;
}
void set(int pos) {
//wordNumber值得是数据pos应该存放在int[1000]数组的哪一个int索引块内,
// 比如int[0]可以存放0-31个数字
// int[1]可以存放32-63个数字
// 具体来说我先找到应该存放在哪一个int块中,然后在找存放在该int块中的哪一个bit
int wordNumber = (pos >> 5);//除32,确定存放在哪一个块
int bitNumber = (pos & 0x1F);//和31进行&操作,确定存放在块中哪一个bit里面 0x1F就是31
bitset[wordNumber] |= 1 << bitNumber;//存放数据时,应该保留之前的数据
}
}
}
OK,《算法通关村第十五关——超大规模数据场景青铜挑战笔记》结束,喜欢的朋友三联加关注!关注鱼市带给你不一样的算法小感悟!(幻听)
再次,感谢鱼骨头教官的学习路线!鱼皮的宣传!小y的陪伴!ok,拜拜,第十五关第二幕见!