目录
首先我们来看一道题,这是一道笔试题。
给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数 中。
1. 遍历,时间复杂度O(N)
2. 排序(O(NlogN)),利用二分查找: logN
当用二分查找,比如AVL树,最多要查找高度次。所以log40亿=32,最多要查找32次.它们都很快,但是这里有一个大麻烦,1G=1024*1024*1024byte,那么1G大概是10亿字节,一个整数要4个字节,那么10亿个整数大约要4G,所以40亿个整数要占16G的空间。16G在内存中已经块存不下了。
并且哈希表和红黑树这种还有附带消耗,比如红黑树的节点中不仅要存key值,还要存三叉链:左孩子、右孩子、父亲指针和颜色,这样就16个字节了(指针都是4个字节在32位下,颜色用的是枚举,也是4个字节)。用哈希表的话,每个节点还要存next指针,也要消耗4个字节。并且还有一个表的消耗,消耗也不小。
3.位图
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,我只需要判断在还是不在即可。那么可以使用一个二进制比 特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。
位图
那么如果40亿个值存不下了,就会在磁盘文件当中存储。
40亿个整数,每个值一个比特位,那么就是40亿个比特位,要占0.5G也就是500M。刚才我们已经直到40亿个整数要存16G,一个整数是4字节,一个字节8byte,那么一个整数是32byte,16G/32byte=0.5G=500M。500M对于内存是可以承受的。
但是40亿个整数不能只开40亿个比特位,需要按照整数的范围42亿来开,接近43亿。32位下,一个整数是4字节,4字节是32byte,那么整数的范围大小有2^32,一共42亿个。那么在这个40亿个整数中,有可能存在41亿的数,也有可能存在42亿的数。所以位图开比特位需要按照整数范来开,需要开42亿个比特位。
位图的实现
在C++中我们无法开辟比特位,但是我们可以开数组,开一个char类型的数组,每个数组元素类型是char,一个char是1字节=8byte,如果要开N个字节,那么就要开N/8个char类型的数组。或者也可以开int类型数组,这样每个数组元素存放的元素比特位更大,一个int=4字节=32byte,那么要开辟N个比特位,就要开辟N/32个int类型数组。
所以位图的私有成员变量可以是vector<char> _bits;也可以是vector<int> _bits;我们这里存char来举例。
如果按char存,不管它大端还是小端,它的第一个vector存的是0-7个比特位,第二个vector寸的是8-15个比特位,以此类推。如果是int类型的第一个vector存的是0-31个比特位,一个vector存32byte。
最开始我们要构造初始化这个vector,需要resize开辟空间,在上面我们提到给数组开辟空间,要开N/8个大小的char类型数组。但是这样的话,如果遇到不是8倍数的N,加入N是10,10/8之后结果转