海量数据问题汇总
bitset结构解海量数据的问题。
位图是内存中连续二进制bit,用于对海量数据的去重和查询。
char是一个字节,8个位。如果要把整数10映射到位图,需要找到第二个char数据,然后再找到第二个char的第二个bit。
1G=1024M 1M=1024KB
bitset存储二进制位
bitset就像一个bool类型的数组一样,但是有空间优化
bitset中的一个元素一般只占1bit,相当于一个char元素所占空间的八分之一
bitset中的每个元素都能被单独访问,例如对于一个叫foo的bitset,表达式foo[3]访问它的第4个元素
整数类型和布尔数组都能转化成bitset。
其实和vector<bool>没有区别啊。
给定1亿个整数,设计算法找到只出现一次的整数。
定义两个位图。
一次都没有出现,在两个位图中都表示0;
只出现一次,在位图一标记位1,在位图二标记为0
出现过两次及两次以上的整数,在位图一标记为1,位图二标记为1
一个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次
使用两个位图,分别是位图1和位图2
数据出现0次,两个位图都记录为0
1次,位图1中记录1,位图2记录为0
2次,位图1中记录0,位图2记录为1
3次及三次以上,位图1和2都记录1
美团第三题,给定两个数组,每个元素表示文件被修改第N次的范围,求同时被修改的文件号
(和下面的区别就在于这里会重复遍历一个文件)
设置两个bitset,首先遍历第一个改变的,不改变bitset1是0,改变bitset1是1
然后遍历第二个改变的,如果bitset1是1,并且bitset2是0,加入并且把bitset2置为1
如果bitset1是1,并且bitset2是1,不动。
但是ACM模式输入的不能是常量啊,所以只能使用vector<bool>来取代这个结构哦。
给定两个文件,分别有100亿个整数,如何找到两个文件的交集
将第一个文件中的所有数据映射到位图,如果存在则为1,不存在则为0
然后将第二个文件中的所有数据和位图进行对比,如果对比到的位置是1,说明该数是两个整数的交集
然后将该交集的数据放在第三个文件中
布隆过滤器解海量数据问题。
用多个哈希函数,将一个数据映射到位图结构中。这种方法不仅可以提升查询效率,也可以节省大量的空间。
布隆过滤器主要是把字符串等其他数据映射到位图中,但是位图只能映射整数,所以我们需要通过哈希函数把字符串变量转换成整数,然后映射到位图中。但是不同的字符串利用哈希函数转换为整数有可能冲突,导致不同字符串映射到位图的相同位置。为了减少这种冲突,可以利用不同的哈希函数将字符串转换为不同的整数,再将转换的整数都映射到位图里面。例如,“张三”转换为20、45、89,然后将转换的这三个整数都映射到位图中。如果要判断张三这个字符串是否存在,就需要判断这3个整数是否都存在,如果其中一个整数不存在,则该字符串就不存在。
#include <bitset> #include <iostream> #include <vector> using namespace std; template<size_t N> class BitSet { private: vector<char> v;//一个v public: BitSet() { v, resize(N / 8 + 1);//开始N个bit } void Set(size_t x)//将x映射到位图当中 { //找相对应的位图下标 int index = x / 8 + 1;//x在位图中的第几个char中 int place = x % 8; v[index] |= (1 << place); } void ReSet(size_t x)//删除x在位图中的映射 { int index = x / 8 + 1;//x在位图中的第几个char中 int place = x % 8; v[index] &= (~(1 << place)); } bool Test(size_t x) //判断一个数据是不是在位图中 { //找到相对应的位图下表 int index = x / 8 + 1; int place = x % 8; return v[index] & (1 << place); } }; struct BKDRHash { BKDRHash() { } size_t operator()(const string& str) { size_t hash = 0; for (auto ch : str) { hash = hash * 131 + ch; } return hash; } }; struct APHash { APHash() { } size_t operator() (const string str) { register size_t hash = 0; size_t ch; for (long i = 0; i < str.size(); i++) { ch = str[i]; if ((i & 1) == 0) hash ^= ((hash << 7) ^ ch ^ (hash >> 3)); else hash ^= (~((hash << 11) ^ ch ^ (hash >> 5))); } return hash; } }; struct DJBHash { DJBHash() { } size_t operator()(const string& str) { size_t hash = 5381; for (auto ch : str) { hash += (hash << 5) + ch; } return hash; } }; template<size_t N, class K = string, class Hash1 = BKDRHash, class Hash2 = APHash, class Hash3 = DJBHash> class BloomFilter { private: BitSet<N> _bitset; public: void Set(const K& s)//对字符串建立映射 { size_t i1 = Hash1()(s) % N; size_t i2 = Hash2()(s) % N; size_t i3 = Hash3()(s) % N; _bitset.Set(i1); _bitset.Set(i2); _bitset.Set(i3); } bool Test(const K& s) { size_t i1 = Hash1()(s) % N; size_t i2 = Hash2()(s) % N; size_t i3 = Hash3()(s) % N; if (!_bitset.Test(i1)) return false; if (!_bitset.Test(i2)) return false; if (!_bitset.Test(i3)) return false; return true; } };
TopK问题:分治(哈希算法)+HashMap/Trie树+小顶堆或大顶堆。
(1)有一个1g大小的文件,里面的每一行都是一个词,词的大小不超过16字节,内存限制大小1M。返回频数最高的100词。
顺序读取文件每一行,利用哈希算法映射到2000个小文件。对每个小文件,使用trie树统计每个词的词频。使用小顶堆计算每个小文件的top100。使用归并,计算2000和top100的top100。
(2)海量日志数据,提取某日访问百度次数最多的ip。
IP是32位,使用哈希算法存到1000个小文件里面。使用HashMap统计频率,找到最大的频率。使用小顶堆计算1000个文件里面的最大频率对应的IP。
(3)海量数据分布在100台电脑,高效统计出TOP10。
虽然数据是分布的,但相同数据可能在不同电脑。因此有些总数属于TOP10的,可能在某台机器的分布只排TOP11。
因此应该使用哈希算法进行电脑重分配。然后使用HashMap统计每台电脑数据频率。然后使用最小堆在每台电脑求TOP10。
然后组合100台机器的TOP10,再使用最小堆求TOP10。
(4)一个有一万行单词的文本文件,要求统计出其中最频繁出现的前10个词。
一万行不算多,不用分割文件。然后使用Trie树统计词的频数。然后使用小顶堆求出TOP10。如果行数多的话就要增加一个步骤:使用哈希算法分割成小文件。再对每个小文件循环上面操作。
(5)一百万个数找出最大的100个。像刷题一样,可以使用快排、堆排。堆排的效率比较高。
重复问题:bitmap位图/布隆过滤器/哈希集合,每个元素对应一个位处理。
(1)给定a/b两个文件,各自存放50亿个URL地址,每个地址各占64个字节,内存限制是4G,找出两个文件公共的URL。
方案一:每个文件的大小大概是5G*64=320G,远远超过内存大小,考虑分而治之的方法。
可以先遍历文件a,使用哈希算法分散到1000个小文件,文件b使用同样的哈希算法分散到1000个小文件。只有相同编号的小文件才可能有相同的元素。接下来,读取a文件某编号的所有URL到Hash_map,然后遍历b中同样编号的所有URL,看是不是再那个Hash_map里面。如果存在,输出文件。
方案二:如果允许有一定错误率,可以使用布隆过滤器,使用位数组。
4G内存大概可以表示340亿bit。将其中一个文件的URL使用布隆过滤器映射为这340亿个位,然后挨个读取另外一个文件的URL。检查是不是在布隆过滤器。如果是,那么该URL应该是共同的(有一定错误率)。
(2)在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。
使用哈希算法分割成小文件。然后利用Hash_set,在小文件找出不重复的整数,再进行归并。
(3)一个文件包含40亿个整数,找出不包含的一个整数。
对于32位的整数,一共有2^32个,每个数对应一个bit,一共需要0.5GB的内存。遍历文件,将每个数对应的位置为1,最后查找位为0的拼凑起来就可以。
排序问题:外排序/bitmap位图。分割文件+文件内排序+文件间归并。
(1)有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求按照query的频度进行排序。(不是像TOPK问题,只要得到TOPK的那种堆只要固定几个就好的解法,是要全部都排序)
首先进行分治,顺序读取每一行,使用哈希算法将它们全部都重新分配到十个文件。使用Hash_map记录(query和query_count),调入内存,利用快速排序/堆排序/归并排序等内部排序算法进行排序,输出到十个文件。再对十个文件进行归并排序。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?