位图 && 布隆过滤器

位图 && 布隆过滤器

1.哈希的应用

1.1位图

位图概念:

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

面试题:

给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】

  1. 遍历,时间复杂度O(N)

  2. 排序(O(NlogN)),利用二分查找: logN

  3. 位图解决

而且前两种方式可能会在内存装不下,因为一个int4字节。40亿个数就是16G。

数据是否在给定的整形数据中,结果是在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。比如:

image-20250320215747120

1.2位图的实现

bitset.h:

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


namespace wzf
{
	// 位图的模拟实现
	class bitset
	{
	public:
		bitset(size_t N) //开辟N个比特位
		{
			// 由于没有开辟空间的单位是一个比特位,因此这里开空间一定会有浪费
			_bits.resize(N/32 + 1, 0);//用resize可以将所有位都初始化为0
			// +1的话,最多浪费31个位。
		}


		//将数据映射到位图中存储
		void set(size_t x)
		{
			// 要先计算出x在那个数字中,x是60,就要存到第60位比特位,就是属于1这个下标的整形中
			size_t index = x / 32; // /32是因为用的int存储,如果数组是char,那就/8
			size_t pos = x % 32; // 知道在那个整形中,就要知道在该整形的第几个位
			_bits[index] |= (1 << pos); // 让这个位置1,表示x被存储到该位图中
			//一定要注意,这里的左移<< 不是方向!是向高位移动!
			// 方向跟大小端存储有关系!但是机器是大端还是小端我们是不知道的! 
			_nums++;
		}

		// 将数据映射到位图中,取消存储【删除】
		void reset(size_t x)
		{
			size_t index = x / 32; // 找到在那个int中
			size_t pos = x % 32; // 找到在那个位
			_bits[index] &= ~(1 << pos); // 把该位置0
			_nums--;
		}

		// 判断数据是否在位图中
		bool find(size_t x)
		{
			size_t index = x / 32;
			size_t pos = x % 32;
			//if (_bits[index] & (1 << pos))
			//{
			//	// 进入该分支就是该位置为1
			//	return true;
			//}

			//return false;

			// 直接一步到位
			return _bits[index] & (1 << pos);
		}

	private:
		vector<int>  _bits; // 用一个数组来当位图.
		size_t _nums; // 统计该位图一共映射了几个数字
	};
}

测试代码:

	// 测试模拟实现的位图接口能否正常运行
	void test_bitset()
	{
		// 开辟100个位,但是实际上开的比这多,100/32 + 1。开个4个int,4*32=128个位
		bitset bs(100); 
		bs.set(12);
		bs.set(88);
		bs.set(98);
		bs.set(19);
		bs.set(99);
		bs.reset(98);
		
		for (int i = 0; i < 100; i++)
		{
			printf("[%d]在位图的存储情况:%d\n", i, bs.find(i));
		}
	}

	// 将腾讯面试题解决
	void FinishTest()
	{
		// 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中。【腾讯】
		// 由于存储的是无符号整数。范围是0 ~ 42亿多[无符号整数的最大存储值]
		bitset(-1); // 构造函数的参数是size_t,这里-1的补码传进去刚好就是无符号整数的最大数

	}

1.3位图的应用

  1. 快速查找某个数据是否在一个集合中

  2. 排序 + 去重

  3. 求两个集合的交集、并集等

  4. 操作系统中磁盘块标记【Linux中的文件系统,还有信号集】

2.布隆过滤器

2.1布隆过滤器的场景

我们在使用新闻客户端看新闻时,它会给我们不停地推荐新的内容,它每次推荐时要去重,去掉 那些已经看过的内容。问题来了,新闻客户端推荐系统如何实现推送去重的? 用服务器记录了用 户看过的所有历史记录,当推荐系统推荐新闻时会从每个用户的历史记录里进行筛选,过滤掉那 些已经存在的记录。 如何快速查找呢?

  1. 哈希解决,缺点:使用哈希来存储用户数据,会浪费空间,当数据量过大,占用空间也会特别多
  2. 位图解决:缺点:位图只能解决整形,如果数据是字符串,那么位图就无法正确的映射存储关系

因此,布隆过滤器此时就需要被用到了。将哈希和位图结合到一起就是布隆过滤器

2.2布隆过滤器的概念

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

image-20250321160737850

注意:

这里之所以一个值要映射到位图的多个位上,是因为当一个值(如string)根据字符串哈希函数映射到某个位上的时候,很可能有一个不一样的值,但是通过哈希函数得到的映射位置是一样的。这个时候就会导致这个值无法被映射到位图中。

因此当一个数据在被判断是否存在于位图的时候,就可能被误判!【应该映射的位置被其他数据映射了】而这个问题无法杜绝,只能缓解

所以布隆过滤器会选择用多个哈希函数,将一个值映射到多的位上。减少误判的概率

详解布隆过滤器的原理,使用场景和注意事项

2.3布隆过滤器的实现

2.3.1布隆过滤器的插入

布隆过滤器在插入的时候要考虑一下哈希函数的数量,数量越多,效率越低,但是冲突率也会越低。这里的实现采用三个哈希函数

image-20250321185029083

	void set(const K& key)
	{
		// 利用哈希函数找到key在位图的位置
		//在布隆过滤器中,哈希函数越多,效率越低,但是太少误报率会高
		// 这里取3个哈希函数,然后将得到的3个哈希地址全部在位图中置1

		// 这里也要注意了,该布隆过滤器的长度是数据量的4倍,因此哈希地址注意越界的问题
		size_t index1 = Hash1()(key) % _len;  // %上该过滤器的长度,确保不会越界
		size_t index2 = Hash2()(key) % _len;
		size_t index3 = Hash3()(key) % _len; // 中间的()代表构造了一个匿名对象

		_bs.set(index1);
		_bs.set(index2);
		_bs.set(index3);
	}
2.3.1布隆过滤器的查找

布隆过滤器的思想是将一个元素用多个哈希函数映射到一个位图中,因此被映射到的位置的比特位一定为1。所以可以按照以下方式进行查找:分别计算每个哈希值对应的比特位置存储的是否为零,只要有一个为零,代表该元素一定不在哈希表中,否则可能在哈希表中

注意:布隆过滤器如果说某个元素不存在时,该元素一定不存在,如果该元素存在时,该元素可能存在,因为有些哈希函数存在一定的误判。

比如:在布隆过滤器中查找"abcd"时,假设3个哈希函数计算的哈希值为:1、3、7,刚好和其他元素的比特位重叠,此时布隆过滤器告诉该元素存在,但实该元素是不存在的。

	bool test(const K& key)
	{
		// 一样通过哈希函数算出哈希地址,然后找到在位图中的映射位置,然后判断是否为1
		size_t index1 = Hash1()(key) % _len; //要注意哈希地址是否越界的问题
		size_t index2 = Hash2()(key) % _len;
		size_t index3 = Hash3()(key) % _len;

		// 这个输出用来测试
		//printf("该key的哈希地址:%zu, %zu, %zu\n", index1, index2, index3);
		
		// 由于有3个哈希函数,因此必须3个哈希地址的位都为1,才说明该key存在于布隆过滤器
		if (_bs.test(index1) == false)
			return false;
			
		if (_bs.test(index2) == false)
			return false;

		if (_bs.test(index3) == false)
			return false;

		return true;
	}
2.3.1布隆过滤器的删除

布隆过滤器不能直接支持删除工作,因为在删除一个元素时,可能会影响其他元素。

比如:删除上图中"tencent"元素,如果直接将该元素所对应的二进制比特位置0,“baidu”元素也被删除了,因为这两个元素在多个哈希函数计算出的比特位上刚好有重叠。

一种支持删除的方法:

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

2.3.4完整代码+测试代码

直接看代码。思路和注意的点都在代码的注释了

// 利用仿函数将string类型转化为int类型的哈希地址,然后映射
struct Strhash1
{
	// BKDRHash算法 
	const size_t operator()(const string& s)
	{
		size_t hashadd = 0;
		for (auto& c : s)
		{
			hashadd *= 131;
			hashadd += c;
		}

		return hashadd;
	}
};

struct Strhash2
{
	//BKDRHash的变种——SDBM(一种简单的数据库引擎)中被应用而得名,它与BKDRHash思想一致,只是种子不同而已。
	const size_t operator()(const string& str)
	{
		size_t hash = 0;
		for (int i = 0; i < str.size(); i++)
		{
			hash *= 65599;
			hash += str[i];
		}
		return hash;
	}
};

struct Strhash3
{
	//Unix system系统中使用的一种著名hash算法,后来微软也在其hash_map中实现。  
	size_t operator()(const string& str)
	{
		if (str.empty())//以保证空字符串返回哈希值0  
			return 0;
		register size_t hash = 2166136261;
		for (auto& c : str)
		{
			hash *= 16777619;
			hash ^= c;
		}
		return hash;
	}
};

//要映射的数据类型是K, 一般来说都是字符串这种位图无法直接映射的。字符串使用场景很多
template<class K = string, class Hash1 = Strhash1, class Hash2 = Strhash2, class Hash3 = Strhash3>
class BloomFilter
{
public:
	// 布隆过滤器长度越长,误报率越低。浪费空间越多
	// 一个般来说取数据量的3~5倍比较好
	BloomFilter(size_t N)
		:_bs(4*N) 
		,_len(4*N)
	{}



	bool test(const K& key)
	{
		// 一样通过哈希函数算出哈希地址,然后找到在位图中的映射位置,然后判断是否为1
		size_t index1 = Hash1()(key) % _len; //要注意哈希地址是否越界的问题
		size_t index2 = Hash2()(key) % _len;
		size_t index3 = Hash3()(key) % _len;

		// 这个输出用来测试
		//printf("该key的哈希地址:%zu, %zu, %zu\n", index1, index2, index3);
		
		// 由于有3个哈希函数,因此必须3个哈希地址的位都为1,才说明该key存在于布隆过滤器
		if (_bs.test(index1) == false)
			return false;
			
		if (_bs.test(index2) == false)
			return false;

		if (_bs.test(index3) == false)
			return false;

		return true;
	}

private:
	wzf::bitset _bs; // 底层就是一个位图
	size_t _len; // 表示该布隆过滤器的长度
};

测试代码:

void test_BloomFilter()
{
	BloomFilter<string> bf(100); //用户需要用该BF存储100个数据【在该BF中实际会开400】
	bf.set("abcd");
	bf.set("aadd");
	bf.set("bacd");
	bf.set("aaag");

	// 如果只有一个哈希函数,那么只有"abcd"能真正存入,其他都会被误判
	cout << bf.test("abcd") << endl;
	cout << bf.test("aadd") << endl;
	cout << bf.test("bacd") << endl;
	cout << bf.test("aaag") << endl;

	// 为了更好的理解,在test函数运行的时候 将这四个数据的哈希地址打印出来
	/*
	"abcd"的哈希地址:74, 2, 277
	"aadd"的哈希地址:244, 0, 11
	"bacd"的哈希地址:204, 0, 119
	"aaag"的哈希地址:254, 6, 55
	*/

}

2.4布隆过滤器的优缺点

  • 优点:
  1. 增加和查询元素的时间复杂度为:O(K), (K为哈希函数的个数,一般比较小),与数据量大小无关

  2. 哈希函数相互之间没有关系,方便硬件并行运算

  3. 布隆过滤器不需要存储元素本身,在某些对保密要求比较严格的场合有很大优势

  4. 在能够承受一定的误判时,布隆过滤器比其他数据结构有这很大的空间优势

  5. 数据量很大时,布隆过滤器可以表示全集,其他数据结构不能

  6. 使用同一组散列函数的布隆过滤器可以进行交、并、差运算

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

3.海量数据处理

3.1 哈希切割

给一个超过100G大小的log file, log中存着IP地址, 设计算法找到出现次数最多的IP地址?

将log file切割成1000个小文件,然后通过通过一个字符串哈希函数计算出一个哈希地址size_t i = hashstr(query) % 1000;,然后将IP地址映射到第i个文件中。这样相同的IP地址一定在一个小文件中!然后把每个文件都加载到内存中

由于这里涉及到统计次数,因此采用map<string, int>,依次让第i个小文件加载到map中,把当前文件出现次数最多的ip地址的次数记到一个max中,读取后面的第i个文件中,如果遇到出现次数比max多的就覆盖max、从而找到出现次数最多的IP地址

image-20250322103545546

与上题条件相同,如何找到top K的IP?如何直接用Linux系统命令实现?

**TopK就是找到出现次数前K个多/少的IP,就是建立对应的容量为K的小堆/大堆。**如:统计次数前K个多的IP,建立容量为K的小堆,堆顶最小,下面的一定比堆顶大,那就是出现次数前K个多的IP

3.2 位图应用

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

2^30次方就是1G,就是10亿,因此这里有10G * 4 大概40G个字节

将数据的出现分为三种情况。

  1. 一次都没出现

  2. 只出现一次

  3. 出现了两次及以上

因此只需要改进一下位图,两个比特位表示一个数。

  1. 00——一次都没出现
  2. 01——只出现一次
  3. 10出现两次及以上

具体来说可以创建两个位图,让两个位图共同达成一个位图的效果。一个位图专门记载00,01,10的第一位。第二个位图专门记载后一位。

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

**方法1:**将文件1的整数映射到一个位图中,然后读取另外一个文件2的数,判断是否在该位图中存在,存在的话就是交集。消耗内存500M

注意:虽然有100亿个数,但是开辟位图只需要开一个size_t能存储的最大数232,因为是整数,范围就是0~232次方。也就是42亿多,即位图开500Mb左右的大小

**方法2:**将文件1和文件2的数据分别映射到两个位图,然后按位与、消耗内存1G

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

还是设计2个位图,和第一题的结构一样,00表示出现0次,01表示出现1次,10表示出现2次,11表示出现三次及以上。这样的话,位图中10和01都是不超过两次的

3.3 布隆过滤器

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

注意:一般query都是sql查询语句或者是网络请求的url,一般是一个字符串。并且一个query平均是 30-60字节,100亿个query就是300G-600G

**近似算法:**将文件1的query映射到一个布隆过滤器,读取文件2的query,判断在不在布隆过滤器。

缺点:不精确,布隆过滤器的特性会导致,不属于交集的数据混到交集中。【因为某个数据在查询的时候,很可能没有映射进去,但是在查询的时候其映射的位都是1,所以判断为交集】

精确算法:

  • 法1:两个文件都很大,分为A、B文件。平均切割1000份,一共2000份,每份大概是300-600M,然后就可以加载到内存中了。然后将其中一个小文件A1,装到一个set中,然后取B1来查询是否存在交集。

缺点:一个A1小文件,要和B1B1000的小文件都对比一次,尽管装进set提升搜索效率,但是每个A的小文件都要和B1B1000对比,这样要比1000*1000次【IO次数】

  • 法2:哈希切割

将每一个query都通过一个字符串哈希函数计算出一个哈希地址size_t i = hashstr(query) % 1000;,算出来这个i是多少,就放到A、B文件对应的Ai、Bi的小文件。比如i是2,那就放到对应A2、B2中。这样每一个query都会映射到属于自己的小文件。【从A取出来的query就映射到Ai,B取的就映射Bi】

然后再找交集的时候,只需要让每一个小文件A1去和B1当中找交集即可,也就是Ai去Bi当中找交集。加载到内存的时候仍然是将数据装进一个set中。这样找交集的时候效率高(O(logN))【比如将A1数据加载到一个set中,让B1的数据去查该set是否存在交集】

image-20250322095840539

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

严格来说布隆过滤器是不能删除元素的。

但是如果一定要改造布隆过滤器使他支持删除的话,那就是——计数器。有点类似于Linux中struct file当中的文件描述符的引用计数。

具体就是将每个位都标记成计数器,一旦被一个数据通过哈希地址映射过来,那就+1,两个数据映射就为2。这样删除的时候-1即可。

注意:这个计数器大小最好就是2个字节。一共可以统计216次方个数。如果是一个字节就太小了,就只能统计28次方,也就是256个。但是如果给太多的话,占据的空间就大了。而布隆过滤器本身的优势就是占据内存空间少,节省空间这个优势

image-20250322090126366

4.一致性哈希

实际上的一致性哈希比我这里说的复杂的多,这里简化一下:

实际上,哈希的应用非常广,就拿朋友圈举例。每个人在微信当中都是一个用户,那就要有相对于的信息存储在微信中,比如一个人的微信号和他的朋友圈信息。微信要给用户提供快速访问朋友圈的功能,那怎么做到用户一点就是自己的朋友圈呢?数据都是存储在服务器上的。其实就是哈希、如下图所示:

image-20250322111449911

但是上面的这个解决方案是有问题的!因为随着用户量的增长,数据在不断的变大!也就是服务器的数量要变多,可能从10W台变成15W台。那这样的话原先的哈希映射关系就不对了!就需要重新迁移数据,但是这么多的数据,重新映射的代价是很大的。

因此这个时候一致性哈希就出来了**。在当初计算哈希地址的时候,就不要%10w,直接%2^32次方(也可以是其他数,就是要大一些)**。这样不管怎么换服务器,当初映射的关系都是对的。就不需要进行批量的数据迁移了、如下图所示:

image-20250322115218911

一个范围的数据映射到一个服务器上,如果新添加了一个服务器,就让负载较重的服务器分出一部分范围的数据,映射到新服务器上(部分的数据搬迁)。这样新服务器也有自己的映射数据范围了

比如<x1x2,T1>,即属于x1x2范围内的数据都是映射到T1机器上

  • 总结:

一致性哈希就是给一个特别大的数,计算哈希地址的时候,不会因为哈希表长度的变长(增加了服务器数量)而需要重新映射大量的数据。

<think>好的,用户想了解C++位图布隆过滤器的相关知识及总结,我需要结合提供的引用资料来组织回答。首先,我应该先明确位图布隆过滤器的基本概念,然后讨论它们的实现和应用,最后总结它们的优缺点和应用场景。 首先看位图部分。根据引用[3],位图用于处理海量数据的存在性判断,比如40亿个整数去重。位图通过每个bit位表示一个数据是否存在,节省空间。需要提到位图的数据结构设计,比如用vector<bool>或直接操作比特位。实现部分可能需要给出C++的代码示例,比如设置某位为1,清除为0,检查存在性。应用方面包括快速查找、去重、排序等,引用[2]和[3]都有提到这些应用。 然后是布隆过滤器。根据引用[4],布隆过滤器是概率型数据结构,用于判断元素可能存在或一定不存在。它使用多个哈希函数映射到不同的位,引用[3]和[4]都提到了这一点。实现部分需要说明插入和查找的过程,以及为什么不能删除,或者如何支持删除(可能需要引用计数)。应用方面,引用[5]提到了缓存穿透的解决方案,防止数据库被无效查询压垮。此外,分布式系统和爬虫中的URL去重也是常见应用。 需要注意布隆过滤器的优缺点,比如空间效率高但存在误判率,无法删除元素等。引用[4]和[5]都有相关描述。可能需要比较位图布隆过滤器的适用场景,比如位图适合精确判断且数据范围小的情况,而布隆过滤器适合大数据量且允许一定误判的情况。 代码实现部分,位图可以用bitset或手动操作位,布隆过滤器则需要多个哈希函数和位图的组合。需要确保示例代码正确,比如展示如何设置位,检查存在性,以及布隆过滤器的插入和查询步骤。 最后总结部分,需要将位图布隆过滤器的核心点对比,强调它们在海量数据处理中的重要性,并结合引用中的内容,如引用[1]提到的结合位图布隆过滤器的方案,可能可以提到这种混合结构在优化空间和查询速度上的优势。 现在需要检查是否有遗漏的引用内容,比如引用[1]提到结合位图布隆过滤器的优点,这可能是在实际应用中的优化策略,需要在总结部分提到。同时,用户要求生成相关问题,需要根据内容设计相关问题,比如应用场景、实现细节、优缺点比较等。 需要注意回答的结构,先讲位图,再讲布隆过滤器,每个部分包括概念、实现、应用,最后总结对比。确保引用标识正确添加,例如在提到布隆过滤器概念时加上[^4],在缓存穿透时引用[^5]等。</think>### C++位图布隆过滤器总结 #### 一、位图(Bitmap) **1. 概念与实现** 位图通过每个比特位(bit)表示一个数据的存在性(0/1),适用于海量数据的快速存在性判断[^3]。例如,40亿个整数(约16GB)用位图仅需约500MB内存。 **C++实现示例**: ```cpp class Bitmap { private: std::vector<uint32_t> bits; // 使用uint32_t数组存储位 public: Bitmap(size_t range) : bits((range >> 5) + 1, 0) {} // 计算需要多少32位块 void set(size_t x) { size_t index = x >> 5; // 确定数组下标 size_t offset = x % 32; // 确定比特位偏移 bits[index] |= (1 << offset); } bool test(size_t x) { size_t index = x >> 5; size_t offset = x % 32; return (bits[index] & (1 << offset)) != 0; } }; ``` **2. 应用场景** - **数据去重**:如统计40亿整数中不重复的数字。 - **快速查询**:判断IP是否在黑名单中。 - **排序**:对有限范围的整数进行非比较排序[^2]。 --- #### 二、布隆过滤器(Bloom Filter) **1. 概念与实现** 布隆过滤器通过$k$个哈希函数将元素映射到多个位,若所有对应位为1则“可能存在”,否则“一定不存在”[^4]。牺牲一定准确性换取极高的空间效率。 **C++实现核心逻辑**: ```cpp class BloomFilter { private: Bitmap bitmap; size_t k; // 哈希函数数量 public: BloomFilter(size_t size, size_t k) : bitmap(size), k(k) {} void add(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); bitmap.set(hash % bitmap.size()); } } bool contains(const std::string& key) { for (size_t i = 0; i < k; ++i) { size_t hash = std::hash<std::string>{}(key + std::to_string(i)); if (!bitmap.test(hash % bitmap.size())) return false; } return true; // 可能存在(有一定误判率) } }; ``` **2. 应用场景** - **缓存穿透防护**:拦截不存在的数据请求,保护数据库。 - **分布式系统**:减少节点间冗余数据传输。 - **爬虫URL去重**:避免重复抓取已处理的页面[^3]。 **3. 优缺点** - **优点**:空间效率高,查询时间$O(k)$,适合海量数据[^4]。 - **缺点**:误判率随元素增加而上升,且不支持删除(除非引入计数布隆过滤器)。 --- #### 三、对比与总结 | **特性** | **位图** | **布隆过滤器** | |------------------|------------------------------|------------------------------| | **数据范围** | 适用于整数且范围较小 | 支持任意数据类型 | | **误判率** | 无 | 有(可调整哈希函数数量优化) | | **删除支持** | 直接修改位即可 | 需额外结构(如计数位图) | | **典型场景** | 精确存在性判断 | 允许误判的存在性预筛 | **混合优化方案**:结合位图布隆过滤器,例如用位图处理高频数据,布隆过滤器处理低频数据,提升整体性能[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值