『 C++ 』海量数据处理

8 篇文章 0 订阅

请添加图片描述


🦖 快速找出海量数据中是否存在该整型数据

请添加图片描述

在上篇文章『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)-CSDN博客中提到一题关于腾讯的面试题;

  • 存在40亿个不重复的无符号整数,且无符号整数没排过序,给定一个无符号整数,如何快速判断一个数是否存在40亿数据当中;

这种题型为判断一个数据是否存在的问题,在C++STL当中首先可以考虑set,unordered_set用于判断数据是否存在;

但是这题出现了另一个关键字 40亿 ;

  • 这意味着数据不能直接存储至内存当中

    40亿个无符号整型在32位的机器下大小约为16GB;

    在一般的机器下无法能够完全将该数据量的数据存储至内存当中;

当然也可以使用以下思路:

  • 排序+二分查找

    先对数据进行排序,再使用二分查找的思路对数据进行甄别;

    排序的时间复杂度最理想的情况其时间复杂度为O(NlogN);

    二分查找的时间复杂度为logN;

  • 逐个数据进行遍历

    使用逐个数据进行遍历的方式时间复杂度为O(N);

尽管上述方式都能使其在40亿个数据中判断该数据是否存在,但整体效率过低;

在这种情况下即可以使用在『 C++ - STL 』位图(BitMap)与布隆过滤器(Bloom Filter)-CSDN博客 文章中所使用的位图;

以其使用位运算的方式将原数据大小为16GB缩减至512MB,且使用位图的时间复杂度基本上为常数级别O(1);


🦖 有限内存情况下两个文件(海量query)中找出交集

请添加图片描述

  • 存在两个文件,分别有100亿个query,我们只有1GB内存,如何找到两个文件交集?

    假设1个query的大小为50byte,一个文件100亿个query的大小则为 5000亿byte;

    5000亿byte换算大小大约为500GB;

在这种情况下可以使用 分割 的方式将文件 分割 为若干个小文件;

以当前情况为例,该题中限制内存大小为1GB,则可以将文件 分割 为若干个约500MB大小的小文件;

再将各个小文件放置于内存当中的set或者unordered_set容器当中判断其交集;

但是若是将文件进行平均切分,在遍历时只能采用暴力枚举的方式,对于检查的效率将会低至 O(N2) ,即每个文件都需要遍历另一组的所有小文件从而找出其中的交集;

  • 而在这种情况为了避免该情况发生可以使用类似哈希桶的方式使大文件分割成小文件

    遍历每个大文件,并使用一个哈希函数将文件中的各个query进行区分至各个小文件内;

    如: i = HashFunc(query) % 1000

在使用该方式之后只需要将Ai文件与Bi文件放置不同的setunordered_set并寻找交集即可;

在使用该方法对两个大文件进行处理的优势是,由于使用了哈希函数所以具有冲突或者完全相同的query将会放置在对应下标的小文件当中,从而可以使得进行比较寻找交集,一般这种方式被称作 哈希切割 ;

但是使用这种方式也会出现对应的问题:

  • 将数据放置在setunordered_set当中可能会出现 抛异常 bad_alloc

    这是因为采用 哈希切割 的切割方式,该方式并不是使得文件像上文所述的 平均切 方式从而导致了单个文件过大而不能完全加载进内存当中;

    以该图为例(A1文件与B1文件过大);

在这种文件过大的情况下通常有两种情况:

  • 文件内存在大量不同且冲突的query;
  • 文件中存在大量相同的query;

若是出现文件内存在大量不相同且出现哈希冲突的query时则换另一个哈希函数对该小文件重新进行 哈希切割 再次分成若干个小文件即可即可;

而文件中存在大量相同的query时,由于set或者unordered_set容器具有去重的功能,所以在一般情况下文件中存在大量相同query的情况时并不会抛出内存异常,只需要插入至对应的set或者unordered_set容器即可;

  • 那么如何控制使得可以灵活进行 哈希切割 或是载入内存?

    set容器与unordered_set容器在插入数据时,将会出现三种情况:

    • 插入成功

      当数据插入成功时将会返回pair,其中pair内存在对应的迭代器以及true;

    • 插入失败

      当数据存在时将会返回pair,其中pair内存在对应的迭代器以及false;

    • 插入失败(抛异常)

      当内存不够时程序将抛出异常bad_alloc;

    只需要灵活对异常进行处理则可以实现灵活进行 哈希切割 或是载入内存进行后续操作;


🦖 海量数据中找出只出现1次的数据

请添加图片描述

  • 存在100亿个整数,找出只出现一次的整数;

该题为找出只出现一次的数据;

那么该思路可以使用 位图 进行解答;

因为数据量为100亿,而整型的最大值为 232-1,故数据中必然存在大量的重复数值,而位图当中可以精确计算数据对应的是否存在问题,其最大数据为 232-1 ;

而位图中由于数据的存在表示为二进制01进行表示,故在此处只使用一个位图并不能很好的解决该问题;

而若是使用两个位图进行映射则可以表示4种情况,分别为00,01,10,11;

class bits{
    public:
  		void set(const int n);
    	void reset(const int n);
    private:
    std::bitset<-1> _bits1;
    std::bitset<-1> _bits2;
};

根据数据的情况以此对两个位图分别进行set操作即可;

在该题中可以将数据分为以下情况:

  • 数据不存在

    当数据不存在时不需要进行set操作,其对应映射为00;

  • 数据存在一个

    当数据对应的映射为00时遍历到该数据时将其对应映射位置set01,表示该数据出现一次;

  • 数据存在两个及以上

    当数据对应的映射为01时再次遍历到相同数据将其对应映射位置set10,表示数据出现过不止一次;

    当数据持续出现时且当前对应数据映射为10时保持不变(由于只需要判断只出现过 1次 的数据,不需要其他冗余操作);

最后检查位图对应的映射即可找出只出现一次的数据;


🦖 有限内存情况下两个文件(整型)找出交集

请添加图片描述

  • 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?

该题与上文中 有限内存情况下两个文件(海量query)中找出交集 题目类似,即在有限内存当中找出两个文件海量数据中的交集;

唯一不同的是该题的数据类型为整型;

由于整型的最大值为 232-1,而给定的文件的数据数据量分别有100亿个,故两个文件中都必然存在大量的重复数值;

在这种情况中只需要使用位图即可以解决:

  • 使用两个位图

    定义两个位图,两个位图都对应着其中一个文件;

    位图可进行映射与去重的操作,可将两个文件遍历并映射至位图当中进行去重;

    最终比较两个位图找出其中对应的交集;

  • 使用一个位图

    定义一个位图,该位图对应两个文件中的其中一个文件;

    遍历一个文件将该文件中的所有数据映射至位图当中;

    再次遍历另外一个文件,当遇到相同值时保存该值(该数据为交集),并将该数据对应的映射置为0,即reset操作;

两种方式对应的都有它的优缺点;

若是不需要频繁找其交集时可采用 方法2 寻找交集;

而若是需要频繁找数据的交集时则可以采用 方法1 从而降低每次访问文件的时间;


🦖 海量数据中找出出现次数不超过2次的数据

请添加图片描述

  • 1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

该题与上文中 海量数据中找出只出现1次的数据 题目类似,且可以使用相同的模型进行解答;

由于在 海量数据中找出只出现1次的数据 题中所采用的模型为2个位图进行实现,其可以控制00,01,10,11四种情况;

在该题中且只需要该几种情况即可;

  • 数据不存在

    当该数据不存在时其对应两个位图的映射为00;

  • 数据出现1次

    当该数据对应的映射为00,且遍历至该数据时,将该数据置为01,表示数据出现1次;

  • 数据出现2次

    当该数据对应的映射为01,且遍历至该数据时,将该数据置为10,表示数据出现2次;

  • 数据出现3次及以上

    当该数据对应的映射为10,且遍历至该数据时,将该数据置为11,表示数据出现3次;

    当数据对应映射位置为11,且遍历至该数据时不采取措施,表示该数据出现3次或3次以上;


🦖 海量数据中找出topK的IP

请添加图片描述

  • 给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?与上题条件相同,如何找到top K的IP?

该题的解法与上文中 有限内存情况下两个文件(海量query)中找出交集 中的解决方式类似;

由于文件过大且类型为字符串类型,故不能采用位图进行操作;

再处理该题时则可以使用上文中出现的 哈希切割 的方式将大文件分成若干个小文件;

使用哈希函数遍历大文件中的IP,并对其使用哈希函数并放置在对应的小文件当中;

将切分后的小文件采用map或者unordered_map统计其出现次数(相同或者冲突的IP将会存放至同一个文件内);

在遍历小文件的过程当中可能会出现抛内存异常bad_alloc;

当出现抛该异常时说明在遍历小文件的过程中某个文件中的数据量过大;

此时说明该小文件中冲突的数据(IP)过多,导致的无法将小文件完全读取至内存当中;

此时只需要换一个哈希函数再次对该小文件进行 哈希切割 成更小的文件进行遍历即可;

并对其使用哈希函数并放置在对应的小文件当中;

将切分后的小文件采用map或者unordered_map统计其出现次数(相同或者冲突的IP将会存放至同一个文件内);

在遍历小文件的过程当中可能会出现抛内存异常bad_alloc;

当出现抛该异常时说明在遍历小文件的过程中某个文件中的数据量过大;

此时说明该小文件中冲突的数据(IP)过多,导致的无法将小文件完全读取至内存当中;

此时只需要换一个哈希函数再次对该小文件进行 哈希切割 成更小的文件进行遍历即可;

而对于题目当中的对于Top K问题则可以建一个小堆Heap从而实现Top K的问题;

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dio夹心小面包

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值