数据结构:位图、布隆过滤器以及海量数据面试题

这篇具有很好参考价值的文章主要介绍了数据结构:位图、布隆过滤器以及海量数据面试题。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1.位图

1.1概念

  1. 引入
    给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。
    (1)遍历: 时间复杂度O(N)
    (2)排序加二分:时间复杂度O(N*logN)
    其中 方法(2)是行不通的,因为内存很难装下这么多数据(40亿整数大概为16G)。
    方法(1)可行,但如果需要多次查询很耗时。

    (3)位图:数据是否在给定的整形数据中,结果是在或者不在,刚好是
    两种状态
    ,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:
    数据结构:位图、布隆过滤器以及海量数据面试题,高阶数据结构,数据结构,算法,笔记,哈希算法,经验分享
    方法(3)的优势的建立起这个位图后,查找任一数据在不在时间复杂度均为O(1),而且只需要 16G / 32 = 0.5G(存储整形需要16G,现在一个比特位就可代表,一个整形有32个比特位),空间占用很小。
  2. 概念
    所谓位图,就是用比特位来存放某种状态(哈希思想的体现),适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在的。

1.2实现

namespace mystd
{
	//叫bitset只是单纯和库里面统一而已,接口命名也是
	//库里面bitset使用基本也是这几个接口
	template<size_t N>  //N表示要表示整数的范围,即比特位个数
	class bitset  
	{
	public:
		bitset() 
		{
			//初始化这里,可能存在浪费,但几个比特位而已
			_bits.resize(N / 32 + 1, 0);
		}

		void set(size_t x)  //设置标记,表示x存在
		{
			//给你x,计算出x属于第几个整数,整数中的第几个比特位
			int i = x / 32;  //第几个
			int j = x % 32;  //第几个比特位
			//把对应的_bits[i]的第[j]位修改为1即可,方法是将1左移j位,或运算即可
			_bits[i] |= (1 << j);
		}

		void reset(size_t x)  //去标记,表示x不存在
		{
			int i = x / 32;
			int j = x % 32;
			//把对应的_bits[i]的第[j]位修改为0即可,方法(1)是将1左移j位 (2)取反(j位置为0,其它位置为1) (3)进行与运算
			_bits[i] &= ~(1 << j);
		}

		bool test(size_t x)  //看x在不在
		{
			int i = x / 32;
			int j = x % 32;
			//取到_bits[i]的第j位即可
			return _bits[i] & (1 << j);  //非0即真,0即假
		}
	private:
		vector<int> _bits;
	};
}

1.3位图应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等

代码:

#include<iostream>
#include<bitset>
using namespace std;
//位图的应用
//快速查找某个数据是否在一个集合中
void test1()
{
	vector<int> a = { 0, 1, 2, 3, 4, 8, 99, 100, 150 };
	bitset<151> bit_set;
	for (auto e : a)
	{
		bit_set.set(e);
	}
	int x = 0;
	while (cin >> x)
	{
		if (bit_set.test(x))
		{
			cout << x << "存在" << endl;
		}
		else
		{
			cout << x << "不存在" << endl;
		}
	}
}


//排序 + 去重
void test2()
{
	int a[] = {0, 1, 2, 3, 4, 8, 99, 99, 100, 100, 150};
	bitset<151> bit_set;
	vector<int> result;
	for (auto e : a)
	{
		bit_set.set(e);
	}
	//实际中应该在建立位图的过程中找出最大最小值,这里就不写了
	//min = 0, max = 150;
	for (int i = 0; i <= 150; i++)   
	{
		if (bit_set.test(i))
		{
			result.push_back(i);
		}
	}
	for (auto e : result)  cout << e << " ";
	cout << endl;
}


//求两个集合的交集、并集等
void test3()
{
	int a1[] = { 0, 1, 2, 3 };
	int a2[] = { 0, 2, 2, 4, 5, 6 };
	//交集
	bitset<7> bit_set1;
	bitset<7> bit_set2;
	for (auto e : a1)  bit_set1.set(e);
	cout << "交集为:";
	for (auto e : a2)
	{
		if (bit_set1.test(e) && bit_set2.test(e) == false)
		{
			bit_set2.set(e);  //过滤掉多次出现的数据
			cout << e << " ";
		}
	}
	cout << endl;

	//并集
	cout << "并集为:";
	bitset<7> bit_set3;  //去掉a1中重复的部分
	for (auto e : a1)
	{
		if (bit_set3.test(e) == false)
		{
			cout << e << " ";
			bit_set3.set(e);
		}
	}
	for (auto e : a2)
	{
		if (bit_set3.test(e) == false)  //把a2特有的提取出来
		{
			cout << e << " ";
		}
	}
	cout << endl;
}



2.布隆过滤器

2.1布隆过滤器的提出

想要判断某个数据在不在:

  1. 哈希表,空间消耗大。
  2. 位图,空间消耗小,只适用于整形。
  3. 哈希位图相结合,即布隆过滤器。可适用各种复杂数据,只要能通过哈希函数转化出关键值即可。

2.2布隆过滤器的概念

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

图解:
数据结构:位图、布隆过滤器以及海量数据面试题,高阶数据结构,数据结构,算法,笔记,哈希算法,经验分享


2.3布隆过滤器的查找

结合前面可知,布隆过滤器的查找需要对多个位置进行判断,都为1才认为存在,有一个为0认为不存在。

  1. 布隆过滤器判断存在,判断不准确

    数据结构:位图、布隆过滤器以及海量数据面试题,高阶数据结构,数据结构,算法,笔记,哈希算法,经验分享
  2. 布隆过滤器判断不在,结果准确

布隆过滤器存在误判,为什么还要使用?

以用户注册为例,用户名等信息存储在服务器数据库,每次注册新用户都要遍历所有数据,消耗太大。这个时候可以考虑使用布隆过滤器,对于判断不在的情况,是准确的,可以允许注册;对于判断在的情况就需要去遍历数据。实际当中可以过滤掉大量的请求,提高效率


2.4布隆过滤器的实现

//三个字符串哈希函数
struct BKDRHash
{
	size_t operator()(const string& key)
	{
		// BKDR
		size_t hash = 0;
		for (auto e : key)
		{
			hash *= 31;
			hash += e;
		}

		return hash;
	}
};

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

struct DJBHash
{
	size_t operator()(const string& key)
	{
		size_t hash = 5381;
		for (auto ch : key)
		{
			hash += (hash << 5) + ch;
		}
		return hash;
	}
};


//布隆过滤器一般都是解决字符串
//关于布隆过滤器的长度,len = N * x,一般x取三以上,至于具体大小依据场景进行衡量
//(1)x过大,空间浪费
//(2)x过小,误判较多(不在判断为在),过滤效果不好
template<size_t N, size_t x = 5, class K = string, class HashFun1 = BKDRHash, class HashFun2 = APHash, class HashFun3 = DJBHash>
class BloomFilter
{
	void set(const K& key)
	{
		//一次标记3个位置,要让数值落在范围内部
		size_t len = N * x;
		size_t hash1 = HashFun1()(key) % len;
		size_t hash2 = HashFun2()(key) % len;
		size_t hash3 = HashFun3()(key) % len;
		_bs.set(hash1);
		_bs.set(hash2);
		_bs.set(hash3);
	}

	bool test(const K& key)
	{
		//看三个位置,有一个为0就返回false
		size_t len = N * x;
		size_t hash1 = HashFun1()(key) % len;
		if (_bs.test(hash1) == false)  return false;
		size_t hash2 = HashFun2()(key) % len;
		if (_bs.test(hash2) == false)  return false;
		size_t hash3 = HashFun3()(key) % len;
		if (_bs.test(hash3) == false)  return false;
		return true;
	}
private:
	bitset<N * x> _bs;
};

2.5布隆过滤器的删除

布隆过滤器一般不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。
数据结构:位图、布隆过滤器以及海量数据面试题,高阶数据结构,数据结构,算法,笔记,哈希算法,经验分享
比如:删除上图中"腾讯"元素,如果直接将该元素所对应的二进制比特位置0,“百度”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。


(了解)一定要支持删除的话,可以采用引用计数的方法:
将布隆过滤器中的每个比特位扩展成一个小的计数器,插入元素时给k个计数器(k个哈希函数计算出的哈希地址)加一,删除元素时,给k个计数器减一,通过多占用几倍存储空间的代价来增加删除操作。


2.6布隆过滤器的优点

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

2.7布隆过滤器的缺点

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



3.海量数据面试题

3.1哈希切分


给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?文章来源地址https://www.toymoban.com/news/detail-754538.html

  1. 100G过大,无法直接在内存中处理,可以先切割成100个小文件。先将IP转化为整形key,然后key %= 100,相同IP会分到一样的小文件。
  2. 依此读取这100个文件,找每个文件中出现次数最多,然后找出最大的那个。
  3. 利用哈希切分可能存在冲突,如果某个小文件极大,可以更换哈希函数,对这个小文件再进行切分。

3.2位图应用

  1. 给定100亿个整数,设计算法找到只出现一次的整数?
    解法一:整形范围为40多亿,位图需要的空间为0.5G,存在出现多次的情况,即三种状态,故一个比特位不够,可以增加一位,即用两个位图实现。当结果为00时代表没有出现、为01时代表出现了一次、为11时代表出现了多次
    解法二:哈希切分,相同的数字一定会分到同个文件,对每个文件做统计即可。
  2. 给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
    (1)先哈希切分,key %= 500,文件一分出A0、A1、A2……A499,文件二分出B0、B1、B2……B499。其中相同的部分(交集)会被分到相同标号的文件,只需要A0对B0、A1对B1……A499对B499的两两找交集就行。
    (2)小文件过大的情况,更换哈希,再次切分即可。
  3. 位图应用变形:1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
    (1)分析状态:一、出现0次。二、出现1次。三、出现2次。四、出现2次以上。
    (2)有四种状态,用两个比特位即可表示,即使用两个位图。当结果为00、01、10时都属于没超过2次,当结果为11时代表结果超过了两次

3.3布隆过滤器

  1. 给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法
    (1)近似算法,先读取文件一,建立布隆过滤器。然后读取文件二,依次判断是否在布隆过滤器中,近似算法存在误判。
    (2)精确算法,对两个大文件进行哈希切分,两两小文件找交集即可。
  2. 如何扩展BloomFilter使得它支持删除元素的操作
    (1)数据量不大,可以用几个位图实现。
    (2)数据量大,一个哈希值对应的就不是一比特位了,而是一个整形。

到了这里,关于数据结构:位图、布隆过滤器以及海量数据面试题的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【数据结构与算法】布隆(Bloom Filter)过滤器

    Redisson系列文章: 【Redisson】Redisson–基础入门 【Redisson】Redisson–布隆(Bloom Filter)过滤器 【Redisson】Redisson–分布式锁的使用(推荐使用) 【分布式锁】Redisson分布式锁底层原理 【Redisson】Redisson–限流器 布隆过滤器(Bloom Filter)是1970年由布隆提出的。它实际上是一个很长的

    2024年02月05日
    浏览(32)
  • 【C++】位图|布隆过滤器|海量数据处理面试题

    所谓位图,就是用每一位来存放某种状态,适用于海量数据,数据无重复的场景。通常是用来判断某个数据存不存在。 首先我们来看一道题目: 给定40亿个不重复的无符号整数,没有进行排序。现在给一个无符号整形,如何快速判断一个数是否存在这40亿个数中。 现在有三种

    2024年02月13日
    浏览(45)
  • 【C++】海量数据处理面试题(位图和布隆过滤器)

    都是大厂面试题哦~ 文章目录 一.位图面试题     1. 给定 100 亿个整数,设计算法找到只出现一次的整数     2.给两个文件,分别有 100 亿个整数,我们只有 1G 内存,如何找到两个文件交集?     3. 1 个文件有 100 亿个 int , 1G 内存,设计算法找到出现次数不超过 2 次的所有整

    2024年02月07日
    浏览(56)
  • 哈希思想应用【C++】(位图,布隆过滤器,海量数据处理面试题)

       目录 一,位图 1. 位图概念 2.实现 3. 测试题 位图的优缺点 二,布隆过滤器 1). 布隆过滤器提出 2). 概念 3). 布隆过滤器的查找 4). 布隆过滤器删除(了解) 5). 布隆过滤器优点 6). 布隆过滤器缺陷 三,海量数据面试题 1)哈希切割 我们首先由一道面试题来理解位图 给40亿个不

    2024年02月04日
    浏览(44)
  • 位图以及布隆过滤器

    本文主要讲解哈希思想的实际应用,位图和布隆过滤器。 讲解位图之前我们先来解答这样一道腾讯的面试题 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】 很多人立马就想到了用哈希表或者红黑树,因为足够

    2024年02月08日
    浏览(52)
  • 【C++】哈希(位图,布隆过滤器)

    今天的内容是哈希的应用:位图和布隆过滤器 目录 一、位图 1.位图概念 2.位图的应用 二、哈希切分 三、布隆过滤器 1.布隆过滤器的概念 2.布隆过滤器的应用 四、总结   今天的内容从一道面试题开始引入: 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何

    2024年02月01日
    浏览(35)
  • 【C++】位图应用 | 布隆过滤器

    给40亿个不重复的无符号整数,没排过序,给一个无符号整数,如何快速判断一个数是否在这40亿个数中 正常思路: 1.排序 + 二分查找 2.放入 哈希表 或者 红黑树 10亿字节 约等于 1GB 40亿个整数约等于 16GB 如果使用上述的两种方法, 内存不够 哈希 的 直接定址法 的 哈希映射

    2024年02月08日
    浏览(43)
  • 【C++练级之路】【Lv.20】位图和布隆过滤器(揭开大数据背后的神秘面纱)

    快乐的流畅:个人主页 个人专栏:《算法神殿》《数据结构世界》《进击的C++》 远方有一堆篝火,在为久候之人燃烧! 哈希映射 的思想,在实际中有许多运用,之前介绍的 哈希表 是一种经典的应用场景,而今天我们将了解其他的哈希数据结构—— 位图和布隆过滤器 ,它

    2024年04月12日
    浏览(47)
  • 【C++】哈希位图和布隆过滤器

    哈希位图和布隆过滤器都是常用的概率数据结构,用于高效地判断一个元素是否存在于一个集合当中,但它们在实现方法和各自的优缺点上有所区别。 哈希位图 哈希位图(Hash Bitmap)是由一个位数组构成,每个元素(通常是一个整数)被映射到位数组中的某个位置。对于集合

    2024年02月08日
    浏览(49)
  • C++哈希应用——位图布隆过滤器

    用哈希表存储用户记录,缺点是需要消耗较大的内存;用位图存储用户记录,缺点是位图一般处理整形,内容是字符串或者自定义类型就很勉强。基于以上,若将哈希和位图结合,称为布隆过滤器,会不会把上面的问题都解决了呢? 概念 布隆过滤器是一种概率型数据结构。

    2024年02月04日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包