哈希思想应用【C++】(位图,布隆过滤器,海量数据处理面试题)

  

目录

一,位图

1. 位图概念

2.实现

3. 测试题

位图的优缺点

二,布隆过滤器

1). 布隆过滤器提出

2). 概念

3). 布隆过滤器的查找

4). 布隆过滤器删除(了解)

5). 布隆过滤器优点

6). 布隆过滤器缺陷

三,海量数据面试题

1)哈希切割


一,位图

我们首先由一道面试题来理解位图

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在
这40亿个数中。【腾讯】
1. 遍历,时间复杂度O(N)
2. 排序(O(NlogN)),利用二分查找: logN
3. 位图解决
数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0
代表不存在。比如:

40亿无符号整形,我们知道1G大概是10亿个字节,也就是说起码 16G数据,排序,二分查找都需要在内存下进行,16G的内存性价比着实有些低。而我们使用位图的方法,用40亿个比特位来表示这40亿个数是否存在,大概就是要消耗 512MB(40亿bit  ==  5亿字节 == 512MB)的内存。

注意:

1. 位图概念

所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

2.实现

#include <iostream>
#include <vector>
using namespace std;

template < size_t N>
class bitset
{
public:
	bitset()
	{
		_bit.resize(N / 8 + 1, 0); // + 1防止有越界
	}

	// 映射的比特位设置为1
	void set(size_t x)
	{
		size_t spa_bit = x / 8;
		size_t in_bit = x % 8;

		_bit[spa_bit] &= (1 << in_bit);
	}

    // 映射的比特位设置为0
	void reset(size_t x)
	{
		size_t spa_bit = x / 8;
		size_t in_bit = x % 8;
	}

    // 测试x,是否存在
	bool test(size_t x)
	{
		size_t spa_bit = x / 8;
		size_t in_bit = x % 8;
		return _bit[spa_bit] & (1 << in_bit);
		// 返回0: 则不存在
		// 返回一个很大的数:存在
	}

private:
	vector<char> _bit;
};

测试案例:

 再回到题目,我们知道有40亿个数,我们知道这只是数量,不是范围,所以,我们尽量开到无符号的最大值,在开大小的时候我们可以这么来设置:

bitset<-1>  st  或者  bitset<0xffffffff> st;

3. 测试题

1. 给定100亿个整数,设计算法找到只出现一次的整数?
思路:用两个位图存储, 用 00,  01,  10表示0次,1次,两次及两次这三种情况。
位图一储存第一位
位图二储存第二位
template <size_t N>
class two_set
{
public:
	void set(size_t x)
	{
		if (st1.test(x) == false
			&& st2.test(x) == false)
		{// 00 -> 01 情况
			st2.set(x);
		}
		else if (st1.test(x) == false
			&& st2.test(x) == true)
		{// 01 -> 10 情况
			st1.set(x);
			st2.reset(x);
		}
	}
private:
	// 我们用 01,00,10 表示三种情况
	bitset<N> st1; // 记录第一位 
	bitset<N> st2; // 记录第二位
};
2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
方法一:
两个文件用两个位图存储,然后依次比较两个位图之间,42亿次数据即可。
template <size_t N>
class cross_set
{
public:
	void set1(size_t x)
	{
		st1.set(x);
	}

	void set2(size_t x)
	{
		st2.set(x);
	}

	void test()
	{
		for (size_t i = 0; i < N; i++)
		{
			if (st1.test(i) && st2.test(i))
				cout << i << " ";
		}
	}

private:
	bitset<N> st1;
	bitset<N> st2;
};

方法二:
用一个位图存储一个文件的整数,然后用第二个文件的数据来查找位图,如果位图中存在则是交集,同时查找完了的设置为0即可防止重复输出交集。
class cross_set2
{
public:
	void set(size_t x )
	{
		st.set(x);
	}

	void test(size_t x)
	{
		if (st.test(x))
		{
			cout << x << " ";
			st.reset(x); //第一次检测完后设置为0
		}
	}
private:
	bitset<N> st;
};

3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

思想:还是用2个位图,用 00 , 01, 10, 11 表示这四种状态,跟第1题类似。

位图的优缺点

优点:速度快,节省空间。

缺点:只映射整型,像浮点数,string等类不能作为存储,进行映射。

二,布隆过滤器

1). 布隆过滤器提出

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉 那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那些已经存在的记录。 如何快速查找呢?
1. 用哈希表存储用户记录,缺点:浪费空间
2. 用位图存储用户记录,缺点:位图一般只能处理整形,如果内容编号是字符串,就无法处理了。
3. 将哈希与位图结合,即布隆过滤器。

2). 概念

布隆过滤器是由布隆(Burton Howard Bloom)在1970年提出的 一种紧凑型的、比较巧妙的 率型数据结构,特点是 高效地插入和查询,可以用来告诉你 “某样东西一定不存在或者可能存 在”,它是用多个哈希函数,将一个数据映射到位图结构中。此种方式 不仅可以提升查询效率,也 可以节省大量的内存空间

哈希的详细知识点,请查看本篇文章: 详解布隆过滤器的原理,使用场景和注意事项 - 知乎 (zhihu.com)

上面博客里面的这张图也展示其中效率与哈希函数个数的关系:
使用布隆过滤器,优点之一是节省空间。虽然哈希函数个数越多,冲突的概率越低;但占用的平均内存也会提高

然后就是当我们的哈希函数为3时,过滤器的长度与插入个数的关系。(K是哈希函数个数,m是长度,n是插入个数)

 

根据里面提供的关系可以得出, 布隆过滤器长度应是插入个数的 5 倍

3). 布隆过滤器的查找

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找: 分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中
注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。
比如:在布隆过滤器中查找"alibaba"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其他元素的比特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的。

4). 布隆过滤器删除(了解)

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。

一种支持删除的方法:将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。
缺陷:
1. 无法确认元素是否真正在布隆过滤器中
2. 存在计数回绕

关于哈希函数的选择,我们参考这篇大佬hash函数算法博客的推荐:各种字符串Hash函数 - clq - 博客园 (cnblogs.com)

// 三个哈希函数

struct APHash
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (long i = 0; i < str.size(); i++)
		{
			size_t ch = str[i];
			if ((i & 1) == 0)
			{
				hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
			}
			else
			{
				hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
			}
		}
		return hash; 
	}
};

struct BKDRHash
{
	size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (auto e : str)
		{
			hash += (size_t)e;
			hash *= 31;
		}
		return hash;
	}
};

struct DJBHash
{
	size_t operator()(const string& str)
	{
		if (!str[0])   // 这是由本人添加,以保证空字符串返回哈希值0  
			return 0;
		size_t hash = 5381;
		for (auto e : str)
		{
			size_t ch = size_t(e);
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};

template <size_t N, class K = string,
class Hash1 = BKDRHash,
class Hash2 = APHash,
class Hash3 = DJBHash>
class bloom_set
{
public:
	// 插入
	void set(const string&  str)
	{
		Hash1 hash1;
		size_t len = N * time;
		st.set(hash1(str) % len);

		Hash2 hash2;
		st.set(hash2(str) % len);

		Hash3 hash3;
		st.set(hash3(str) % len);
	}

	// 判断是否存在
	// 1. 如果其中一个不存在,那么一定不存在
	// 2. 如果三位置都存在,那么可能存在,需要确认
	bool test(const string& str)
	{
		Hash1 hash1;
		Hash2 hash2;
		Hash3 hash3;
		size_t len = N * time;
		if (!st.test(hash1(str) % len))
			return false;

		if (!st.test(hash2(str) % len))
			return false;

		if (!st.test(hash3(str) % len))
			return false;

		return true; // 都存在,那可能存在
	}
private:

	static const size_t time = 5;  // 过滤器长度与插入个数关系
	bitset < N * time > st;
};

5). 布隆过滤器优点

1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关。
2. 哈希函数相互之间没有关系,方便硬件并行运算。
3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势。
4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势。
5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能。
6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算。

6). 布隆过滤器缺陷

1. 有误判率,即存在假阳性(False Position),即不能准确判断元素是否在集合中。(补救方法:再建立一个白名单,存储可能会误判的数据)
2. 不能获取元素本身。
3. 一般情况下不能从布隆过滤器中删除元素。
4. 如果采用计数方式删除,可能会存在计数回绕问题。

三,海量数据面试题

1)哈希切割

例1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法?
精确算法:
分析:query可以理解为一个字符串,100亿个字符串,假设一个query的平均长度为50byte, 也就是差不多5000亿byte(500G)每个文件,装入内存是不可能的。我们采取的方法是:
1. 分成多份 。 将这500G的数据进行切割,分成多份(本题我们分成1000份)
2. 两个文件中的每个query经过相同的哈希函数处理,进入1000份中的其中一个文件。
3. 在经过1000次比较两组文件中相对应的数据,把结果填入一个文件中即可。
但我们在真正实现的时候又会出现各种各样的问题,比如, 这两个大问题。
无法保证,每个文件的大小。
我们预计每个小文件500MB左右,但有时一个数据多重复,因此会
假设:B3被分到了3G,我们需要进行对B3进行再次切割,但B3的状况我们会遇见两种:
1. B3有一种query大量重复,无法进行切割。
2. B3中有大量不同,可以切割。
解决方案:
直接使用unordered_set/ set 来插入目标小文件。
如果插入2.5G的内存,一定会报内存不足的问题,而这个就是 情况2,出现这个我们可以更换哈希函数,对小文件进行再次进行切割。
如果插入2.5G的内存,没有内容不足的问题,那么就是情况1。
例2:给一个超过100G大小的log file, log中存着IP地址, 只有1G的内存,设计算法找到出现次数最多的IP地址?
本题跟上一题类似:
1. 我们先将100G文件分成多份,假设分成100份,然后通过哈希函数进行分类写入小文件中。
2. 将小文件写入map / unordered_map中,如果写入成功,则选出出现最多的id,其余的数据clear。如果写入不成功(小文件过大),则换哈希函数再次切割,重复步骤2。
3. 到最后map中留下的数据,插入vector进行排序即可。

下期预告: C++11 !!!

结语

   本小节就到这里了,感谢小伙伴的浏览,如果有什么建议,欢迎在评论区评论,如果给小伙伴带来一些收获请留下你的小赞,你的点赞和关注将会成为博主创作的动力

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/200729.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C语言你爱我么?(ZZULIOJ 1205:你爱我么?)

题目描述 LCY买个n束花准备送给她暗恋的女生&#xff0c;但是他不知道这个女生是否喜欢他。这时候一个算命先生告诉他让他查花瓣数&#xff0c;第一个花瓣表示"爱"&#xff0c;第二个花瓣表示"不爱"&#xff0c;第三个花瓣表示"爱"..... 为了使最…

【Openstack Train安装】七、glance安装

Glance是为虚拟机的创建提供镜像的服务&#xff0c;我们基于Openstack是构建基本的IaaS平台对外提供虚拟机&#xff0c;而虚拟机在创建时必须为选择需要安装的操作系统&#xff0c;Glance服务就是为该选择提供不同的操作系统镜像。Glance提供Restful API可以查询虚拟机镜像的me…

计算机网络(超详解!) 第二节 物理层(上)

1.物理层的基本概念 物理层考虑的是怎样才能在连接各种计算机的传输媒体上传输数据比特流&#xff0c;而不是指具体的传输媒体。 物理层的作用是要尽可能地屏蔽掉不同传输媒体和通信手段的差异。 用于物理层的协议也常称为物理层规程(procedure)。 2.物理层的主要任务 主要…

Linux处理文本常见命令

目录 1 vim 2 echo 3 tee 4 cat 1 vim 编辑文本类的内容&#xff0c;使用的时候 vim [文件名]&#xff0c;比如 vim A.txt 进入vim界面后&#xff0c;按i可以开启编辑模式&#xff0c;按ESC可以关闭编辑模式&#xff0c;关闭编辑模式后:wq!保存并退出 2 echo ech…

PHP:处理数据库查询数据

注&#xff1a; DB_num_rows($result5)可以替换为mysqli_num_rows($result5) DB_fetch_array($result5)可以替换为mysqli_fetch_assoc($result5) 一、查询单个数据 代码解析 1、SQL语句 查询表www_users中当userid等于变量$_SESSION[UserID]时的depart_code值 $sql &qu…

【JavaEE初阶】 HTTP 请求 (Request)详解

文章目录 &#x1f340;序言&#x1f384;认识URL&#x1f6a9;URL 基本格式&#x1f6a9;query string&#x1f6a9;关于 URL encode &#x1f334;认识 "方法" (method)&#x1f6a9;GET方法&#x1f6a9;POST 方法&#x1f6a9; GET 和 POST 的区别 &#x1f38b;…

7 种 JVM 垃圾收集器详解

一、概述 如果说收集算法是内存回收的方法论&#xff0c;那么垃圾收集器就是内存回收的具体实现。Java虚拟机规范中对垃圾收集器应该如何实现并没有任何规定&#xff0c;因此不同的厂商、版本的虚拟机所提供的垃圾收集器都可能会有很大差别&#xff0c;并且一般都会提供参数供用…

如何利用软文打动消费者,媒介盒子支招

软文与一般文案的差别就在于它的目的性十分强烈&#xff0c;写软文不难&#xff0c;但是想要写出打动消费者的软文还需要一定的技巧。它需要根据目标受众来输出&#xff0c;接下来媒介盒子就为大家分享&#xff1a;如何用软文提升产品购买率。 一、 故事打动用户 没人会不爱看…

接口测试【加密解密攻防完整版】实战教程详解

一、对称加密 对称加密算法是共享密钥加密算法&#xff0c;在加密解密过程中&#xff0c;使用的密钥只有一个。发送和接收双方事先都知道加密的密钥&#xff0c;均使用这个密钥对数据进行加密和解密。 数据加密&#xff1a;在对称加密算法中&#xff0c;数据发送方将明文 (原…

1 NLP分类之:FastText

0 数据 https://download.csdn.net/download/qq_28611929/88580520?spm1001.2014.3001.5503 数据集合&#xff1a;0 NLP: 数据获取与EDA-CSDN博客 词嵌入向量文件&#xff1a; embedding_SougouNews.npz 词典文件&#xff1a;vocab.pkl 1 模型 基于fastText做词向量嵌入…

抖音、视频号流行的 Bokeh(虚化) 效果是怎么实现的?

未经作者(微信ID:Byte-Flow)允许,禁止转载 文章首发于公众号:字节流动 什么是 bokeh 效果? Bokeh 效果是指照片中背景模糊而主体清晰的一种摄影效果。这种效果是通过使用大光圈的镜头来实现的,使得光圈外的景物失去焦点,呈现出一种柔和、虚化的效果。 Bokeh 效果的质量…

30万起售的阿维塔12能卖的动吗?

作者 | 魏启扬 来源 | 洞见新研社 今年前十个月&#xff0c;累计交付1.76万辆&#xff0c;这就是阿维塔11交出的成绩单。 作为一个拥有长安汽车和宁德时代作为资源支撑&#xff0c;华为提供技术支持的品牌&#xff0c;阿维塔11平均每个月不到2000辆的销量水平显然有失水准。 …

科研绘图配色

01 配色的基本原则 颜色需要有自身的意义。不同的颜色表示不同的分组&#xff0c;相近的颜色表示同一个分组&#xff1b;配色需要展现数据逻辑关系&#xff0c;突出关键数据&#xff0c;比如重要的数据用深色或暖色表示&#xff0c;不重要的数据用浅色或冷色表示。 色彩种类两…

Redis 基础、字符串、哈希、有序集合、集合、列表以及与 Jedis 操作 Redis 和与 Spring 集成。

目录 1. 数据类型 1.1 字符串 1.2 hash 1.3 List 1.4 Set 1.5 sorted set 2. jedis操作redis 3. 与spring集成 1. 数据类型 1.1 字符串 String是最常用的数据格式&#xff0c;普通的kay-value都归结为此类&#xff0c; value值不仅可以是string&#xff0c;可以是数字…

【c语言:常用字符串函数与内存函数的使用与实现】

文章目录 1. strlen函数1.1使用1.2模拟实现 2.strcmp函数2.1使用2.2模拟实现 3.strncmp函数3.1使用3.2模拟实现 4.strcpy函数4.1 使用4.2模拟实现 5.strcncpy5.1使用5.2模拟实现 6.strcat函数6.1使用6.2模拟实现 7.strncat函数7.1使用7.2模拟实现 8.strstr函数8.1使用8.2模拟实…

ffmpeg 免安装,配置环境变量

1、下载ffmpeg https://download.csdn.net/download/qq284489030/88579595 2、解压 解压ffmpeg-4.4-essentials_build.zip到目标文件夹&#xff0c;比如 d:\apps下&#xff1b; 3、配置环境变量 &#xff08;1&#xff09;电脑桌面鼠标右键点击“此电脑”&#xff0c;弹出…

[带余除法寻找公共节点]二叉树

二叉树 题目描述 如上图所示&#xff0c;由正整数1, 2, 3, ...组成了一棵无限大的二叉树。从某一个结点到根结点&#xff08;编号是1的结点&#xff09;都有一条唯一的路径&#xff0c;比如从10到根结点的路径是(10, 5, 2, 1)&#xff0c;从4到根结点的路径是(4, 2, 1)&#x…

C++算法入门练习——数据流第K大元素

现有一个初始为空的序列S&#xff0c;对其执行n个操作&#xff0c;每个操作是以下两种操作之一&#xff1a; 往序列S中加入一个正整数x&#xff1b;输出当前序列S​中第k​大的数。 其中&#xff0c;第k大是指将序列从大到小排序后的第k个数。 利用stl里的priority_queue自动…

如何让电脑每天定时自动关机?

如何让电脑每天定时自动关机&#xff1f;电脑已经成为社会生产活动中不可或缺的一种工具&#xff0c;它对于我们每个人都非常的重要&#xff0c;不管是工作、生活还是学习中&#xff0c;我们都需要利用电脑。不过很多小伙伴因为繁忙或者因为其它的事情&#xff0c;导致电脑经常…

Vue3水印(Watermark)

APIs 参数说明类型默认值必传width水印的宽度&#xff0c;默认值为 content 自身的宽度numberundefinedfalseheight水印的高度&#xff0c;默认值为 content 自身的高度numberundefinedfalserotate水印绘制时&#xff0c;旋转的角度&#xff0c;单位 number-22falsezIndex追加…