哈希——位图、布隆过滤器

news2024/7/6 21:12:19

在这里插入图片描述

欢迎来到Cefler的博客😁
🕌博客主页:那个传说中的man的主页
🏠个人专栏:题目解析
🌎推荐文章:题目大解析(3)

在这里插入图片描述


目录

  • 👉🏻位图概念
    • bitset
  • 👉🏻位图的实现
    • 将某数据在位图的某比特位置设置为1存在
    • 将某数据在位图的某比特位置设置为0不存在
    • 检测某某数据在位图的某比特位置是否为1
    • complete code
  • 👉🏻设计位图找到只出现一次的整数
  • 👉🏻布隆过滤器
    • 概念
    • 多个哈希函数的原因:
    • 布隆过滤器的查找
    • 布隆过滤器删除
    • 布隆过滤器优缺点
    • 布隆过滤器简单实现

👉🏻位图概念

在C++中,哈希位图(Hash Bitmap)是一种数据结构,用于高效地判断一个元素是否存在于集合中。它通常用于解决查找和去重的问题。

哈希位图基于位操作实现,使用一个位数组来表示集合的元素状态。每个元素对应位数组中的一个位,如果该位被设置为1,表示该元素存在于集合中;如果该位为0,则表示该元素不存在于集合中。

哈希位图的基本思想是利用哈希函数将元素映射到位数组的索引位置。当需要插入一个元素时,先通过哈希函数计算出其对应的索引位置,在位数组中将该位设置为1。当需要查询一个元素是否存在时,同样通过哈希函数计算出索引位置,并检查位数组中对应位的值。

哈希位图的优点是占用空间较小,因为它只使用一个比特位来表示一个元素的存在与否。相比于传统的数组或链表,哈希位图在空间复杂度上更加高效。此外,哈希位图在插入、查询等操作上也具有较好的性能,时间复杂度通常为O(1)

然而,哈希位图也存在一些限制。首先,它只适用于元素的取值范围较小且离散的情况,因为位数组的大小与元素的最大值有关。当元素取值范围较大时,位数组的空间消耗也会增加。其次,哈希函数的选择对于哈希位图的性能影响较大,一个好的哈希函数应该能够均匀地将元素分布到位数组中。

总的来说,哈希位图是一种高效的数据结构,适用于解决查找和去重问题,它在空间和时间复杂度上都具有优势。

bitset

C++ 的 <bitset> 头文件提供了 std::bitset 类,用于表示固定大小的位集合(bit set)。std::bitset 是一个固定大小的位数组,其中的每一位都可以被设置或清除,也可以进行位运算操作。以下是 bitset 头文件的主要用法:

创建和初始化 bitset

#include <bitset>
#include <iostream>

int main() {
    // 创建一个包含 8 位的位集合,所有位初始化为 0
    std::bitset<8> bits1;

    // 使用整数初始化位集合
    std::bitset<8> bits2(255); // 255的二进制表示为 "11111111"

    // 使用二进制字符串初始化位集合
    std::bitset<8> bits3("10101010");

    std::cout << "bits1: " << bits1 << std::endl;
    std::cout << "bits2: " << bits2 << std::endl;
    std::cout << "bits3: " << bits3 << std::endl;

    return 0;
}

访问和修改位

std::bitset<8> bits("11001100");

// 访问位
bool bit5 = bits[5]; // 获取第 5 位的值

// 修改位
bits.set(3, 1);     // 将第 3 位设置为 1
bits.reset(2);      // 将第 2 位设置为 0
bits.flip(1);       // 将第 1 位取反

std::cout << "bits: " << bits << std::endl;

位运算操作

std::bitset<4> bits1("1010");
std::bitset<4> bits2("0110");

// 位与运算
std::bitset<4> result_and = bits1 & bits2; // 结果为 "0010"

// 位或运算
std::bitset<4> result_or = bits1 | bits2;  // 结果为 "1110"

// 位异或运算
std::bitset<4> result_xor = bits1 ^ bits2; // 结果为 "1100"

std::cout << "AND: " << result_and << std::endl;
std::cout << "OR: " << result_or << std::endl;
std::cout << "XOR: " << result_xor << std::endl;

其他操作

std::bitset<8> bits("11001100");

// 统计位集合中设置为 1 的位数
std::cout << "Count of 1s: " << bits.count() << std::endl;

// 测试某一位是否为 1
bool test_result = bits.test(3); // 测试第 3 位是否为 1

// 找到第一个被设置为 1 的位的位置
size_t first_set_bit = bits._Find_first(); 

std::cout << "Test result: " << test_result << std::endl;
std::cout << "First set bit: " << first_set_bit << std::endl;

这只是 std::bitset 的一些基本用法,还有其他方法和操作可供使用。 bitset 是一个非常方便的工具,特别适用于需要高效表示和操作位信息的场景。

👉🏻位图的实现

将某数据在位图的某比特位置设置为1存在

void set(const int& x)
		{
			int i = x / 32;//在位图中的第i个整型
			int j = x % 32;//bit位
			_bits[i] |= (1 << j);
		}

将某数据在位图的某比特位置设置为0不存在

void reset(const int& x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= ~(1 << j);
		}

检测某某数据在位图的某比特位置是否为1

bool test(const int& x)
		{
			int i = x / 32;
			int j = x % 32;

			return _bits[i] & (1 << j);
		}

complete code

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

namespace bit
{
	template<size_t N>
	class bitset
	{
	public:
		void set(const int& x)
		{
			int i = x / 32;//在位图中的第i个整型
			int j = x % 32;//bit位
			_bits[i] |= (1 << j);
		}
		//将某数据在位图的某比特位置设置为0不存在
		void reset(const int& x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= ~(1 << j);
		}
		bool test(const int& x)
		{
			int i = x / 32;
			int j = x % 32;

			return _bits[i] & (1 << j);
		}
	private:
		vector<int> _bits;
	};
}

👉🏻设计位图找到只出现一次的整数

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

namespace bit
{
	template<size_t N>
	class bitset
	{
	public:
		void set(const int& x)
		{
			int i = x / 32;//在位图中的第i个整型
			int j = x % 32;//bit位
			_bits[i] |= (1 << j);
		}
		//将某数据在位图的某比特位置设置为0不存在
		void reset(const int& x)
		{
			int i = x / 32;
			int j = x % 32;
			_bits[i] &= ~(1 << j);
		}
		bool test(const int& x)
		{
			int i = x / 32;
			int j = x % 32;

			return _bits[i] & (1 << j);
		}
	private:
		vector<int> _bits;
	};
	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			//00->01
			//01->10
			//10->11
			//11->不变
			if (_bs1.test(x) == false && _bs2.test(x) == false)
			{
				_bs2.set(x);
			}
			else if (_bs1.test(x) == false && _bs2.test(x) == true)
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
			else if (_bs1.test(x) == true && _bs2.test(x) == false)
			{
				_bs1.set(x);
				_bs2.set(x);
			}
		}

		void Print()
		{
			for (size_t i = 0; i < N; i++)
			{
				if (_bs1.test(i) == false && _bs2.test(i) == true)
				{
					cout <<"1->" << i << endl;
				}
				else if (_bs1.test(i) == true && _bs2.test(i) == false)
				{
					cout <<"2->" << i << endl;
				}
			}
			cout << endl;
		}

	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}

👉🏻布隆过滤器

概念

在这里插入图片描述

布隆过滤器是一种用于判断一个元素是否属于一个集合的概率型数据结构。它在解决大规模数据集合的查找问题上具有一些优势,但也有一些局限性。以下是布隆过滤器存在的主要意义:

意义

  1. 高效的查找操作: 布隆过滤器可以快速判断一个元素是否属于集合,而无需存储实际的元素信息。这使得在大规模数据集合中进行查找操作更为高效。

  2. 节省内存: 布隆过滤器相对于其他数据结构来说,具有较小的内存占用。这是因为它不需要存储实际元素,只需维护一个位数组即可。

  3. 快速插入和删除: 布隆过滤器的插入和删除操作是常数时间复杂度,这使得它在某些场景下比其他数据结构更为高效。

注意!
布隆过滤器,可以用来告诉你 “某样东西一定不存在或者可能存在”

多个哈希函数的原因:

  1. 减少碰撞(冲突)的概率: 哈希函数的目标是将元素均匀地映射到位数组的不同位置。使用多个哈希函数可以减少碰撞的概率,即不同元素映射到相同位置的可能性。

  2. 降低误判率: 布隆过滤器的主要缺点是可能存在误判(false positives),即判断元素存在于集合中,但实际上并不存在。使用多个哈希函数可以降低这种误判的概率,提高布隆过滤器的准确性。

  3. 增强抗攻击性: 多个哈希函数可以增加布隆过滤器对恶意攻击(例如故意构造冲突以使过滤器失效)的抵抗力。

虽然使用多个哈希函数能够提高布隆过滤器的性能和准确性,但也需要权衡计算成本。因为每个哈希函数都需要计算,使用太多哈希函数可能会降低性能,因此选择适量的哈希函数是一项需要仔细考虑的任务。

布隆过滤器的查找

布隆过滤器的查找操作主要用于判断一个元素是否可能存在于集合中。请注意,布隆过滤器存在一定的误判率(false positive),即它可能错误地认为元素存在于集合中,但实际上并不存在。

下面是布隆过滤器的查找过程:

  1. 哈希函数: 首先,将要查找的元素通过多个哈希函数映射到位数组中的多个位置。这些哈希函数应该是独立且均匀分布的,以减小碰撞的概率。

  2. 检查位数组: 对应于这些哈希函数映射的位数组位置上的位,如果所有的位都被设置为1,那么布隆过滤器判定元素可能存在于集合中。

  3. 判断结果: 如果所有对应位置的位都是1,那么布隆过滤器返回一个“可能存在”的结果,表示元素可能存在于集合中。否则,它确定元素一定不存在于集合中。

需要注意的是,如果查找结果是“可能存在”,那么还需要进一步的验证,例如在一个真正的数据存储结构(服务器)中进行实际查找,以确认元素是否确实存在。

由于布隆过滤器的误判率,它适合用于那些对于少数误判可以容忍的场景,例如缓存、防止数据库查询等。然而,在要求绝对准确性的场景下,不适合使用布隆过滤器

布隆过滤器删除

布隆过滤器(Bloom Filter)是一种设计用来加速元素是否存在于集合中的数据结构,但它不支持直接删除元素。这是因为删除元素可能影响到其他元素的映射,从而导致不准确的结果。

具体来说,如果要删除布隆过滤器中的某个元素,会涉及将元素对应的位数组位置重置为0。然而,这会影响到其他元素,因为可能存在多个元素映射到同一个位数组位置。如果重置一个位,可能会使其他元素的对应位也被重置,从而导致误判。

布隆过滤器优缺点

布隆过滤器(Bloom Filter)是一种用于加速元素是否存在于集合中的数据结构,具有一些明显的优点和一些局限性。以下是布隆过滤器的主要优缺点:

优点

  1. 快速查询: 布隆过滤器在判断元素是否存在于集合中的查询操作上非常高效,通常为常数时间复杂度。

  2. 节省内存: 相对于一些其他数据结构(比如哈希表),布隆过滤器使用的内存较少,因为它只需要维护一个位数组,而不需要存储实际的元素信息。

  3. 高效的插入和删除: 插入和删除操作都是常数时间复杂度,使得布隆过滤器在某些特定场景下比其他数据结构更高效。

缺点

  1. 误判率: 布隆过滤器存在一定的误判率,即可能会将不存在于集合中的元素错误地判断为存在。这是由于多个元素映射到同一位置可能导致的冲突。

  2. 不支持删除: 布隆过滤器不支持直接删除元素。删除元素可能会影响其他元素的映射,导致不准确的结果。

  3. 哈希函数的选择: 布隆过滤器的性能与所选择的哈希函数密切相关。选择不好的哈希函数可能会增加误判率。

  4. 固定大小: 布隆过滤器的大小是固定的,不能动态调整。如果元素数量超过预期,可能需要重新设计和调整布隆过滤器的大小。

  5. 不适用于小数据集: 在小数据集上,误判率相对较高,因此布隆过滤器可能不是最佳选择。

总体来说,布隆过滤器适用于那些对于一定的误判可以容忍的大规模数据集合查询场景,但在对精确性要求高、数据集规模较小或者需要频繁插入和删除元素的场景中可能不合适。

布隆过滤器简单实现

C++中,你可以使用标准库中的 std::bitset 或者其他哈希库来实现布隆过滤器。

以下是一个简单的C++布隆过滤器的实现示例:

#include <bitset>
#include <functional>
#include <iostream>

class BloomFilter {
public:
    BloomFilter(int size) 
    : size(size) 
    {
        filter.reset();
    }

    // 添加元素
    void add(const std::string& element) {
        for (auto& hashFunction : hashFunctions) {
            std::size_t hash = hashFunction(element);
            filter.set(hash % size, true);
        }
    }

    // 检查元素是否存在于集合中
    bool contains(const std::string& element) const {
        for (auto& hashFunction : hashFunctions) {
            std::size_t hash = hashFunction(element);
            if (!filter.test(hash % size)) {
                return false;
            }
        }
        return true;
    }

private:
    int size;
    std::bitset<1000> filter; // 用于存储数据的位集合,根据实际需要调整大小
    std::hash<std::string> hashFunction1;
    std::hash<std::string> hashFunction2;
    // 可以添加更多的哈希函数

    std::hash<std::string> hashFunctions[2] = {hashFunction1, hashFunction2};
};

int main() {
    BloomFilter bloomFilter(1000);

    bloomFilter.add("example");
    bloomFilter.add("test");

    std::cout << "Contains 'example': " << bloomFilter.contains("example") << std::endl; // 输出 1 (true)
    std::cout << "Contains 'hello': " << bloomFilter.contains("hello") << std::endl;     // 输出 0 (false)

    return 0;
}

上述示例中,BloomFilter 类有一个位集合(用 std::bitset 实现),并使用两个哈希函数来将元素映射到位集合中。你可以根据需要添加更多的哈希函数。 add 方法用于添加元素,而 contains 方法用于检查元素是否可能在集合中。


如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注🌹🌹🌹❤️ 🧡 💛,学海无涯苦作舟,愿与君一起共勉成长
在这里插入图片描述
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

曲线拟合:走进数据建模中的艺术与科学

在现代科学和工程领域&#xff0c;曲线拟合是一项重要的数据分析技术&#xff0c;它可以通过数学模型来近似描述实际数据中的复杂关系。本文将详细介绍曲线拟合的基本概念、方法和应用领域&#xff0c;并探究其在数据建模中的艺术与科学。 第一节&#xff1a;曲线拟合的基本概…

UML建模图文详解教程08——部署图

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl本文参考资料&#xff1a;《UML面向对象分析、建模与设计&#xff08;第2版&#xff09;》吕云翔&#xff0c;赵天宇 著 部署图概述 部署图(deployment diagram)也被译作配置…

线程池(用于处理Runnable任务或Callable任务)

一&#xff0c;线程池 二&#xff0c; 如何创建线程池 案例&#xff1a; //1,通过ThreadPoolExecuter创建一个线程池对象ExecutorService pool new ThreadPoolExecutor(3,5,8,TimeUnit.SECONDS,new LinkedBlockingQueue<>(4),Executors.defaultThreadFactory(),new Thr…

Java LCR 089 打家劫舍

题目链接&#xff1a;打家劫舍 定义一个数组 dp&#xff0c;其中 dp[i] 表示从第 0 间房子到第 i 间房子&#xff08;包括第 i 间&#xff09;能够偷窃到的最高金额。 对于第 i 间房子有两种选择&#xff0c;偷或不偷&#xff1a; 偷就不能偷第 i - 1 间房子&#xff1a; dp[i]…

【教学类-06-08】20231125(55格版)X-Y之间“减法-题”(以10-20之间为例)(必须X>Y,题目少)

图片展示 需求&#xff1a; 20以内减法&#xff0c;不需要再练习其中10以内部分&#xff0c;改为10-20以内的减法&#xff0c;X-Y大于10&#xff0c;小于20的所有减法题。 代码展示&#xff1a; “-”减法 X-Y 之间的所有减法-题&#xff08;如10-20之间的所有减法&#xff0…

VSCode 警告:v-on event ‘@toggleClick‘ must be hyphenated

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

VUE留言板

效果预览图 完整代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>作业</title><styl…

快手AI布局:从直播电商到大模型,如何打造智能生态?

快手科技在2023年第三季度业绩中&#xff0c;首次披露了关于AI业务的一些重要信息&#xff0c;显示出其对AI的重视和投入。快手AI的核心业务和竞争优势是什么&#xff1f;AI的发展&#xff0c;对快手业绩带来了哪些方面的提振&#xff1f; 快手AI业务板块&#xff1a;直播电商…

百度AI布局:从财报看百度的核心竞争力和未来发展方向

百度是中国最大的搜索引擎&#xff0c;也是全球领先的人工智能&#xff08;AI&#xff09;公司。百度在2023年第三季度业绩中&#xff0c;展示了其在AI领域的强劲表现和广阔前景。 百度财报透露了关于AI业务的哪些重要信息&#xff1f; 百度在2023年第三季度的财报中&#xf…

1.5 C语言之字符输入输出

1.5 C语言之字符输入输出 一、概述二、字符计数三、行计数四、单词计数五、练习 一、概述 字符文本流&#xff0c;是由多行字符构成的字符序列&#xff0c;而每行字符都由0个或多个字符组成&#xff0c;行末是一个换行符。 标准库提供的输入输出模型&#xff0c;用于读取文本内…

【Python】(自定义类)计算语句执行时间

一个玩具&#xff0c;写着来玩的。 用的time模块&#xff0c;代码很简单(所以才说是个玩具) 代码&#xff1a; import time class TimeStamp:__timestampNone__keyNonedef __init__(self,tipsNone,keyNone):self.__timestamp{}self.NewStamp(tips,key)def NewStamp(self,tips,…

RK3568驱动指南|第八篇 设备树插件-第73章 设备树插件使用实验

瑞芯微RK3568芯片是一款定位中高端的通用型SOC&#xff0c;采用22nm制程工艺&#xff0c;搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码&#xff0c;支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU&#xff0c;可用于轻量级人工…

针对String、StringBuffer、Stringbuilder区别及使用场景

可变性&#xff08;Mutability&#xff09;&#xff1a; String&#xff1a; 字符串是不可变的。一旦创建了一个字符串对象&#xff0c;它的值就不能被修改。任何对字符串的操作实际上都是创建了一个新的字符串对象。 StringBuilder&#xff1a; 字符串生成器&#xff0c;是可…

希尔伯特变换-matlab仿真

希尔伯特变换&#xff08;hilbert transform&#xff09;简介 在信号处理中我们常见的有傅里叶变换&#xff0c;用来分析频域信息&#xff0c;还有拉普拉斯变换和z变换&#xff0c;用于系统分析系统响应。短时傅里叶分析和小波分析用于时频分析。希尔伯特变换似乎听到的比较少…

node fs模板及蓝桥案例实战

文章目录 介绍文件写入writeFile 异步写入writeFileSync 同步写入appendFile / appendFileSync 追加写入createWriteStream 流式写入 文件读取readFile 异步读取readFileSync 同步读取createReadStream 流式读取 文件移动与重命名文件删除文件夹操作mkdir / mkdirSync 创建文件…

DNS/ICMP协议、NAT技术

目录 DNS协议DNS背景域名简介 ICMP协议ICMP功能ping命令traceroute命令 NAT技术NAT技术背景NAT IP转换过程NAPTNAT技术的缺陷NAT和代理服务器 网络协议总结应用层传输层网络层数据链路层 DNS协议 DNS&#xff08;Domain Name System&#xff0c;域名系统&#xff09;协议&…

【送书福利-第二十八期】《从概念到现实:ChatGPT和Midjourney的设计之旅》

&#x1f60e; 作者介绍&#xff1a;我是程序员洲洲&#xff0c;一个热爱写作的非著名程序员。CSDN全栈优质领域创作者、华为云博客社区云享专家、阿里云博客社区专家博主、前后端开发、人工智能研究生。公粽号&#xff1a;程序员洲洲。 &#x1f388; 本文专栏&#xff1a;本文…

软件测试之银行测试详解

一、金融类软件测试 举个栗子&#xff0c;银行里的软件测试工程师。横向跟互联网公司里的测试来说&#xff0c;薪资相对稳定&#xff0c;加班的话想对来说没那么多&#xff08;有些银行加班也挺严重的&#xff09;&#xff0c;但业务稳定。实在是测试类岗位中的香饽饽&#xf…

实现简单的操作服务器和客户端(上)

一、说明 描述:本教程介绍如何使用 simple_action_server 库创建斐波那契动作服务器。此示例操作服务器生成斐波那契序列,目标是序列的顺序,反馈是计算的序列,结果是最终序列。 内容 创建操作消息编写一个简单的服务器 代码

没搞懂二维差分是什么怎么办???

摸鱼的时候画的&#xff0c;根据公式反推 一维差分倒是懂了 a[10]{1,2,6,9,11,12,17,21,32,67}; c[10]{1,1,4,3,2,1,5,4,11,35}; 现要把[3,7]的值都增加3 c[10]{1,1,7,3,2,1,5,1,11,35}; 要查询的时候再用for循环相加 结论&#xff1a;成立且适用于多次修改 不知道为什么这个…