40亿个手机号码如何去重?
一、概述
常见的面试题:文件中有40亿个手机号码,请设计算法对手机号码去重,内存限制1G
二、 排序
最直接简单的方式是对所有的手机号码进行排序,重复的手机号码必然相邻,保留第一个,去掉后面重复的就行。
显然,排序的时间复杂度太高了
三、HashMap
既然直接排序的时间复杂度太高,那就用HashMap,具体思路是把手机号码记录到HashMap中,用HashMap的key唯一来去重。
实际要存40亿手机号码,1G的内存够分配这么多空间吗?显然不行
三、文件切割
显然,这是海量数据问题。工作中遇到过,很自然想到文件切割的方式(大文件拆成小文件),避免内存过大。拆分可以通过hashcode进行拆分,相同的手机号码会被拆分到同一个小文件中,之后在每个小文件去重。
这么多的文件操作,效率自然不高啊。
四、bitmap
可以对hashmap进行优化,采用bitmap这种数据结构,可以顺利地同时解决时间问题和空间问题。在很多实际项目中,bitmap经常用到。 一个unsigned int类型数据可以标识0~31这32个整数的存在与否。
/**
* @author yangyc
* 内存中装入头2位(手机号第一位始终为1,因此只需装入后面2位),需要99(2个9)个bit,
* 99 bit = 99/8 byte =12.375 byte
* BitSet bitSet=new BitSet(99);
*
* 内存中装入手机号码后8位,需要99999999(8个9)个bit
* 99999999 bit = (99999999/8) byte = (99999999 / (8 * 1024)) KB = (99999999 / 8 *1024*1024) M = 11.92M
*
* BitSet bitSet1= new BitSet(999999999);
*/
这样使用位图值为1时,就表明该数是存在的。而且通过bitmap记录,客观上就自动完成了排序功能。
五、总结
使用了 bitmap 之后,数字的排序功能、中位数、top-K问题等扩展问题都解决。一般海量的数据,而且有内存限制的问题都是通过 文件切割(大文件拆成小文件)或者bitmap位图 来解决