276

问答题 276 /376

算法题,10亿个32位正整数,求不同值,只给1GB内存。。。

参考答案

参考回答:
位图法是基于int型数的表示范围这个概念的,用一个bit位来标识一个int整数,若该位为1,则说明该数出现;若该位为0,则说明该数没有出现。一个int整型数占4字节(Byte),也就是32位(bit)。那么把所有int整型数字表示出来需要2^32 bit位的空间,为了方便,我们可以把这些信息每8bit分割保存为byte数组,换算成字节单位也就是2^32 bit/8 = 2^29 Byte,大约等于512MB

具体方案:那么接下来我们只需要申请一个int数组长度为int tmp[N/32+1]即可存储完这些数据,其中N代表要进行查找的总数(这里也就是2^32),tmp中的每个元素在内存在占32位可以对应表示十进制数0~31,所以可得到BitMap表:tmp[0]:可表示0~31,tmp[1]:可表示32~63,tmp[2]可表示64~95,~~

假设这10亿int数据为:6,3,8,32,36,……,那么具体的BitMap表示为:

(1).如何判断int数字放在哪一个tmp数组中:将数字直接除以32取整数部分(x/32),例如:整数8除以32取整等于0,那么8就在tmp[0]上;

(2).如何确定数字放在32个位中的哪个位:将数字mod32取模(x%32)。上例中我们如何确定8在tmp[0]中的32个位中的哪个位,这种情况直接mod上32就ok,又如整数8,在tmp[0]中的第8 mod上32等于8,那么整数8就在tmp[0]中的第八个bit位(从右边数起)。

然后我们怎么统计只出现一次的数呢?每一个数出现的情况我们可以分为三种:0次、1次、大于1次。也就是说我们需要用2个bit位才能表示每个数的出现情况。此时则三种情况分别对应的bit位表示是:00、01、11

我们顺序扫描这10亿的数,在对应的双bit位上标记该数出现的次数。最后取出所有双bit位为01的int型数就可以了。


class BitmapTest {

private static final int CAPACITY = 1000000000;//数据容量

//定义一个byte数组缓存所有的数据

private byte[] dataBytes = new byte[1 << 29];

public static void main(String[] args) {

BitmapTest ms = new BitmapTest();

byte[] bytes = null;

Random random = new Random();

for (int i = 0; i < CAPACITY; i++) {

int num = random.nextInt();

System.out.println("读取了第" + (i + 1) + "\t个数: " + num);

bytes = ms.splitBigData(num);

}

System.out.println("");

ms.output(bytes);

}

/**

*读取数据,并将对应数数据的 到对应的bit中,并返回byte数组

* @param num读取的数据

* @return byte数组  dataBytes

*/

private byte[] splitBigData(int num) {

//获取num数据对应bit数组(虚拟)的索引

long bitIndex = num + (1l << 31);

int index = (int) (bitIndex / 8); //bit数组(虚拟)在byte数组中的索引

//bitIndex在byte[]数组索引index中的具体位置

int innerIndex = (int) (bitIndex % 8);

System.out.println("byte[" + index + "]中的索引:" + innerIndex);

dataBytes[index] = (byte) (dataBytes[index] | (1 << innerIndex));

return dataBytes;

}

/**

*输出数组中的数据

* @param bytes byte数组

*/

private void output(byte[] bytes) {

int count = 0;

for (int i = 0; i < bytes.length; i++) {

for (int j = 0; j < 8; j++) {

if (!(((bytes[i]) & (1 << j)) == 0)) {

count++;

int number = (int) ((((long) i * 8 + j) - (1l << 31)));

System.out.println("取出的第"+ count+"\t个数: "+ number);

}

}

}

}

}