【C++】海量数据面试题

news/2024/12/2 21:38:50/

海量数据面试题

在这里插入图片描述

文章目录

  • 海量数据面试题
  • 一、哈希切割
  • 二、位图应用
    • 1.给定100亿个整数,设计算法找到只出现一次的整数
    • 2.求两个文件交集
    • 3.在100亿个整数中找到出现次数不超过2次的所有整数
  • 三、布隆过滤器
    • 1.求两文件交集(近似算法)
    • 2.求两文件交集(精确算法)
    • 3.计数法使布隆过滤器支持删除

一、哈希切割

给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?以及如何找到top K的IP?如何直接用Linux系统命令实现?

  • 我们可以把这100G大小的文件切分成100个小文件
  • 依次读取ip地址,通过哈希函数把每个ip地址转换为一个整型,再把这些分别%100切分到这100个小文件里头

image-20230413232452174

  • 此时相同的ip地址通过哈希函数转换的整型一定是相同的,最终也一定进入同一个文件,不同的ip也可能进入同一个文件,现在我们使用map容器(map<string, int> countMap)依次对每个小文件统计次数,统计完A0,把A0的次数记录下来为最大次数(pair<ip, int> maxIP),clear旧数据,再统计A1,比较A1的次数和最大次数的大小,更新最大次数,再clear掉旧数据……,依次比较,依次更新最大次数的数据。
  • 如果需要找到top K,就需要用到优先级队列(priority_queue<pair<string,int>,vector<pair<string,int>>,less>),less仿函数将比较pair中int的大小,使用小堆可满足找最大的K个值的条件。

使用上述哈希切割的方法可以找到出现次数最多的IP地址,但是会存在一个小问题:某个文件太大,导致某个相同ip太多,且映射冲突到这个编号文件的ip太多(可能又会导致内存不够了),这里我们可以try-catch捕获,伪代码如下:

try
{countMap[ip]++;
}
catch (exception& e)
{//捕获内存不足的异常,说明内存不够countMap.clear();//针对这个小文件,再次换个哈希函数,进行哈希切分,再切分成小文件//再对小文件依次统计
}

找到topK的Linux的指令如下:

假如有以下文件IP.log:

192.168.5.5
234.23.13.44
10.152.16.23
192.168.3.10
192.168.1.4
192.168.2.1
192.168.0.9
10.152.16.23
192.163.0.9
192.168.0.9 
69.52.220.44 
192.168.1.4 
192.168.1.5
192.163.0.9 
192.168.2.1
192.168.0.1
192.168.0.2

(1)按行排序,并将结果输出到标准输出

sort 文件名

(2)统计并显示文本文件中出现的行或列的次数

uniq -c

(3)根据出现次数倒序排序

sort -r

(4)查看开头K行

head -k

综上:显示出现次数最多的前K个IP

sort log_file | uniq -c | sort -nr | head -k

sort log_file对文件进行排序,使得相同的IP地址聚在一起,接着使用uniq -c进行去重,并将重复的次数显示在每列的旁边,通过这个次数来使用sort -nr进行降序排序,使得出现次数最高的IP地址在前面,然后使用head -k获取前k个IP地址。

运行结果:

image-20230420200321294

image-20230420200346809


二、位图应用

1.给定100亿个整数,设计算法找到只出现一次的整数

给定100亿个整数,设计算法找到只出现一次的整数

题目明确说找到只出现一次的整数,那说明一个整数出现的次数的情况分为如下三种状态:

  1. 出现0次
  2. 出现1次
  3. 出现2次及以上

一个比特位只能表示一种状态,但是两个比特位就能表示4种了,因此我们借助两个比特位来表示如上三种状态:

  • 00(0次)
  • 01(1次)
  • 10(2次)

所以这里我们需要重新写一个位图,两个比特位表示一个值,所以一个char只能表示4个值(8bit / 2),100亿个整数要开2^32 * 2个比特位,大概占1G。

image-20230413162344031

虽然这里是两个比特位表示一个值,不过我们实际操作的时候可以设计一个类,里面封装两个bitset,并且通过后续的位运算来表示两个比特位表示一个值。

image-20230413233702278

具体实现的代码如下:

template<size_t N>
class twobitset
{
public:void set(size_t x){if (!_bs1.test(x) && !_bs2.test(x)) // 00{_bs2.set(x); // 01}else if (!_bs1.test(x) && _bs2.test(x)) // 01{_bs1.set(x); _bs2.reset(x); // 10}// 10 不变}void PirntOnce(){for (size_t i = 0; i < N; ++i){if (!_bs1.test(i) && _bs2.test(i)){cout << i << endl;}}cout << endl;}
private:bitset<N> _bs1;bitset<N> _bs2;
};

简易测试代码如下:

void test_twobitset()
{twobitset<100> tbs;int a[] = { 3, 5, 6, 7, 8, 9, 33, 55, 67, 3, 3, 3, 5, 9, 33 };for (auto e : a){tbs.set(e);}tbs.PirntOnce();
}

2.求两个文件交集

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

这里我们可以设计两个位图,刚好占用1G内存,按照如下方式设置

  1. 把A文件读到位图1,把B文件读到位图2
  2. 遍历两个位图,将位图1和位图2进行与&操作,结果存储在位图1中,此时位图1中映射的整数就是两个文件的交集。

image-20230413235946910


3.在100亿个整数中找到出现次数不超过2次的所有整数

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

此题和第一题的思想是一样的,对于出现的次数,可以用两个比特位来表示,一次表示出现次数的状态:

  1. 出现0次(00)
  2. 出现1次(01)
  3. 出现2次(10)
  4. 出现3次及以上(11)

这里同样需要两个位图来解决,只需要对第一题的代码进行改造即可,最后状态是01或10的整数就是出现次数不超过2次的整数。

template<size_t N>
class two_bitset
{
public:void set(size_t x){int in1 = _bs1.test(x);int in2 = _bs2.test(x);if (in1 == 0 && in2 == 0)//00{_bs2.set(x);//01}else if (in1 == 0 && in2 == 1)//01{//出现次数置为2_bs1.set(x);//10_bs2.reset(x);}else if (in1 == 1 && in2 == 0)//10{_bs2.set(x);//11}}
private:bitset<N> _bs1;bitset<N> _bs2;
};

三、布隆过滤器

1.求两文件交集(近似算法)

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?给出近似算法

题目要求给出近视算法,也就是允许存在一些误判,那么我们就可以用布隆过滤器。

  1. 先读取其中一个文件当中的query,将其全部映射到一个布隆过滤器当中。
  2. 然后读取另一个文件当中的query,依次判断每个query是否在布隆过滤器当中,如果在则是交集(但是会存在误判的风险,不过无法避免,这也是近似算法的特点),不在则一定不是交集。

2.求两文件交集(精确算法)

给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?给出精确算法

注意:

  • 这里明确指出给出精确算法,我们不能使用布隆过滤器,因为会存在误判,要用上文一开始使用的哈希切分来解决。

我们按照如下的规则来进行哈希切割。

  • 我们假设每个query平均20byte,100亿query就是200G,我们可以把这么个文件分为1000个小文件
  • 依次读取query,通过哈希函数把每个query转换为一个整型,再把这些分别%1000切分到这1000个小文件里头,此时文件A就被分为A0 ~ A999共1000个小文件,文件B就被分为B0 ~ B999共1000个小文件。

image-20230413231529747

由于在切分A文件和B文件的时候,使用的是相同的哈希函数,因此A文件和B文件中相同的query计算的是相同的i值,就被分到了对应的Ai和Bi中,因此我们只需要找到A0与B0的交集、A1与B1的交集……最终把这些集合合并起来就是文件A和文件B的总交集。

image-20230413232215183

而小文件找交集的方法也很简单,如下规则:

  • 我们可以将其中一个小文件加载到内存,并放到一个set容器中,再遍历另一个小文件当中的query,依次判断每个query是否在set容器中,如果在则是交集,不在则不是交集。
  • 如果切分出来的一个小文件过大,需要将此文件再次进行切分,再走一遍这个过程即可。

3.计数法使布隆过滤器支持删除

如何扩展BloomFilter使得它支持删除元素的操作?

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。一种支持删除的方法(计数法删除):

  • 将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。

image-20230413164803445

缺陷:

  1. 无法确认元素是否真正在布隆过滤器中
  2. 存在计数回绕

总结:

  • 布隆过滤器不支持直接删除归根结底在于其主要就是用来节省空间和提高效率的,在计数法删除时需要遍历文件或磁盘中确认待删除元素确实存在,而文件IO和磁盘IO的速度相对内存来说是很慢的,并且为位图中的每个比特位额外设置一个计数器,就需要多用原位图几倍的存储空间,这个代价也是不小的。若支持删除就不那么节省空间了,也就违背了布隆过滤器的本质需求。

http://www.ppmy.cn/news/49453.html

相关文章

vue2项目PC端如何适配不同分辨率屏幕

项目构建&#xff1a;基于vue-cli3构建&#xff0c;使用postcss-px2rem px2rem-loader进行rem适配 实现原理&#xff1a;每次打包&#xff0c;webpack通过使用插件postcss-px2rem&#xff0c;帮我们自动将px单位转换成rem单位前方有坑&#xff1a;UI框架部分组件使用JavaScript…

Thinkphp+vued大学生租房管理系统mysql校园房屋租赁网站系统

学生租房管理系统是计算机技术和网络迅速发展的一个大学生租房信息应用解决方案。大学生租房平台将Internet网络技术与现代管理观念相融合&#xff0c;针对信息技术的特点对大学生租房平台进行规划和重构&#xff0c;对大学生租房信息流进行优化及合理配置&#xff0c;生成动态…

云原生(docker+k8s+阿里云)

Gitee-Kubernetes学习 kubectl备忘清单 k8s官方文档-task [云原生-kubectl命令详解] ingress详解 ingress官方文档 云原生-语雀-架构师第一课 如上图&#xff0c;服务器有公网ip和私网ip&#xff0c;公网ip是外部访问服务器用的&#xff0c;重启一次实例就变化了&#xff0c;如…

基于JavaSpringMvc+mybatis实现学生信息管理系统

基于JavaSpringMvcmybatis实现学生信息管理系统 博主介绍&#xff1a;5年java开发经验&#xff0c;专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源码联系方式 文章目…

Infura的基本用途和具体实例

文章目录 Infura 可以做什么&#xff1f;1. 向以太坊网络发送交易并获取交易的结果2. 获取以太坊地址的余额、交易历史记录等信息3. 通过 Web3.js 等以太坊库与智能合约进行交互3. 使用 Infura 发送以太币4. 其他服务 Infura 是一个由 ConsenSys 开发的以太坊基础设施服务提供商…

反向传播推导+numpy实现

很久没有看深度学习了&#xff0c;忘了好多东西。本来想着推导一下&#xff0c;后来发现自己不会了。 再看看以前写的代码&#xff0c;又避开了最终的东西&#xff0c;于是决定重新推导一下。 数据的说明 首先&#xff0c;我们要做一个回归的任务&#xff0c;我们使用numpy随…

《Java8实战》第12章 新的日期和时间 API

原来的Java的时间类Date、java.util.Calendar类都不太好&#xff0c;以语言无关方式格式化和解析日期或时间的 DateFormat 方法也有线程安全的问题 12.1 LocalDate、LocalTime、LocalDateTime、Instant、Duration 以及 Period 12.1.1 使用 LocalDate 和 LocalTime LocalDate…

c++学习之类与对象3

目录 成员变量和函数的存储 this指针 this指针的工作原理 this指针的应用 const修饰的成员函数 友元 友元的语法 1.普通全局函数成为类的友元 2.类的某个成员函数作为另一个类的友元 整个类作为另一个类的友元 运算符重载 1 运算符重载的基本概念 2 重载加号运算符…