深入理解位图(Bit - set):概念、实现与应用

news2025/5/19 5:21:57

目录

引言 

一、位图概念 

(一)基本原理 

(二)适用场景 

二、位图的实现(C++ 代码示例) 

 三、位图应用

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

2. 排序 + 去重 

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

4. 操作系统中磁盘块标记 

四、总结 


引言
 

在处理海量数据时,如何高效地存储和查询数据是一个关键问题。位图(Bit - set)作为一种非常巧妙的数据结构,在很多场景下能够极大地提升数据处理的效率。今天,我们就来深入探讨一下位图的相关知识。
 

一、位图概念
 

(一)基本原理
 


位图,简单来说,就是用每一位来存放某种状态。它特别适用于海量数据且数据无重复的场景,通常用于判断某个数据是否存在。比如,给定40亿个不重复的无符号整数,要快速判断一个无符号整数是否在这40亿个数中。数据是否存在刚好是两种状态(存在或不存在),此时就可以使用一个二进制比特位来代表数据是否存在的信息。如果二进制比特位为1,代表存在;为0,则代表不存在。
 
假设我们有一个整数数组  int array[] = {1,3,7,4,12,16,19,13,22,18};  ,我们可以按照一定规则将这些数映射到位图中。例如,将整数的范围划分为若干段,像0 - 7、8 - 15、16 - 23等,每个整数对应位图中的一个比特位。
 

(二)适用场景
 

1. 快速查找:在海量数据集中快速判断某个数据是否存在,相比传统的遍历查找(时间复杂度O(N) )或者排序后二分查找(时间复杂度O(logN) ),位图在某些情况下可以实现更高效的查找。
2. 数据去重与统计:在统计一些不重复元素的相关信息时,位图可以帮助我们快速标记已经出现过的元素,方便进行去重和统计数量等操作。
 

二、位图的实现(C++ 代码示例)
 


下面是一个简单的位图类  bitset  的C++ 实现代码:

#pragma once
#include<vector>

namespace ldg
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_a.resize(N / 32 + 1);
		}

		// x映射的那个标记成1
		void set(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			_a[i] |= (1 << j);
		}

		// x映射的那个标记成0
		void reset(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

			_a[i] &= (~(1 << j));
		}

		bool test(size_t x)
		{
			size_t i = x / 32;
			size_t j = x % 32;

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

	template<size_t N>
	class twobitset
	{
	public:
		void set(size_t x)
		{
			// 00 -> 01
			if (!_bs1.test(x) && !_bs2.test(x))
			{
				_bs2.set(x);
			} // 01 -> 10
			else if (!_bs1.test(x) && _bs2.test(x))
			{
				_bs1.set(x);
				_bs2.reset(x);
			}
			// 本身10代表出现2次及以上,就不变了
		}

		bool is_once(size_t x)
		{
			return !_bs1.test(x) && _bs2.test(x);
		}
	private:
		bitset<N> _bs1;
		bitset<N> _bs2;
	};
}
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
#include<unordered_set>
#include<unordered_map>
#include<set>
using namespace std;

#include"BitSet.h"

//int main()
//{
//	bit::bitset<1000> bs;
//	bs.set(1);
//	bs.set(10);
//	bs.set(100);
//
//	cout << bs.test(1) << endl;
//	cout << bs.test(10) << endl;
//	cout << bs.test(100) << endl;
//	cout << bs.test(999) << endl<<endl;
//
//	bs.set(999);
//	bs.reset(10);
//
//	cout << bs.test(1) << endl;
//	cout << bs.test(10) << endl;
//	cout << bs.test(100) << endl;
//	cout << bs.test(999) << endl << endl;
//
//	getchar();
//
//	bit::bitset<-1> bs1;
//	//bit::bitset<0xffffffff> bs2;
//
//	getchar();
//
//	return 0;
//}

//int main()
//{
//	int a[] = { 1,2,3,3,4,4,4,4,4,2,3,6,3,1,5,5,8,9};
//	bit::twobitset<10> tbs;
//	for (auto e : a)
//	{
//		tbs.set(e);
//	}
//
//	for (auto e : a)
//	{
//		if (tbs.is_once(e))
//		{
//			cout << e << " ";
//		}
//	}
//	cout << endl;
//}

int main()
{
	int a1[] = { 1,2,3,3,4,4,4,4,4,2,3,6,3,1,5,5,8,9 };
	int a2[] = { 8,4,8,4,1,1,1,1 };

	ldg::bitset<10> bs1;
	ldg::bitset<10> bs2;

	// 去重
	for (auto e : a1)
	{
		bs1.set(e);
	}

	// 去重
	for (auto e : a2)
	{
		bs2.set(e);
	}

	for (int i = 0; i < 10; i++)
	{
		if (bs1.test(i) && bs2.test(i))
		{
			cout << i << " ";
		}
	}
	cout << endl;
}

代码解释
 
1. 构造函数: bitset(size_t bitCount)  根据传入的比特位总数  bitCount  来初始化位图。通过  (bitCount >> 5) + 1  计算出需要的  int  类型数组的大小,因为一个  int  有32位,右移5位(相当于除以32),并加1是为了保证能容纳所有比特位。
2.  set  函数:将指定的比特位  which  置为1。先判断  which  是否越界,然后通过位运算计算出该比特位在  _bit  数组中的索引  index  和在该  int  中的位置  pos  ,最后使用  |=  操作将对应位置置1。
3.  reset  函数:将指定的比特位  which  置为0。同样先判断越界,计算索引和位置后,使用  &=  和按位取反操作将对应位置清零。
4.  test  函数:检测指定比特位  which  是否为1。判断越界后,计算索引和位置,通过与运算判断对应位置是否为1。
5.  size  函数:返回位图中比特位的总个数。
6.  Count  函数:统计位图中为1的比特位的个数。通过一个预先定义好的  bitCnttable  数组来快速统计每个字节中1的个数,遍历  _bit  数组进行累加。
 

 三、位图应用

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

看对应bit位是1是0

#include <vector>
#include <iostream>

class bitset {
public:
    bitset(size_t bitCount) : _bit((bitCount >> 5) + 1), _bitCount(bitCount) {}

    void set(size_t which) {
        if (which > _bitCount) return;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        _bit[index] |= (1 << pos);
    }

    bool test(size_t which) const {
        if (which > _bitCount) return false;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        return _bit[index] & (1 << pos);
    }

private:
    std::vector<int> _bit;
    size_t _bitCount;
};

int main() {
    // 假设集合元素的范围最大到100
    bitset setBits(100);
    // 向集合中添加元素
    int elements[] = {10, 20, 30, 40};
    for (int element : elements) {
        setBits.set(element);
    }
    // 查找元素
    int target = 30;
    if (setBits.test(target)) {
        std::cout << target << " is in the set." << std::endl;
    } else {
        std::cout << target << " is not in the set." << std::endl;
    }
    return 0;
}
 


 
解释:先创建一个  bitset  对象,指定其大小涵盖集合元素可能的范围。通过  set  函数将集合中的元素对应比特位置1 ,之后使用  test  函数传入要查找的元素,根据返回值判断元素是否在集合中。
 

2. 排序 + 去重
 

#include <vector>
#include <iostream>
#include <algorithm>

class bitset {
public:
    bitset(size_t bitCount) : _bit((bitCount >> 5) + 1), _bitCount(bitCount) {}

    void set(size_t which) {
        if (which > _bitCount) return;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        _bit[index] |= (1 << pos);
    }

    bool test(size_t which) const {
        if (which > _bitCount) return false;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        return _bit[index] & (1 << pos);
    }

private:
    std::vector<int> _bit;
    size_t _bitCount;
};

int main() {
    // 假设集合元素的范围最大到100
    bitset setBits(100);
    // 原始数组,包含重复元素
    int arr[] = {10, 20, 10, 30, 20, 40};
    for (int element : arr) {
        setBits.set(element);
    }
    std::vector<int> sortedUnique;
    for (size_t i = 0; i < setBits.size(); ++i) {
        if (setBits.test(i)) {
            sortedUnique.push_back(i);
        }
    }
    std::sort(sortedUnique.begin(), sortedUnique.end());
    for (int num : sortedUnique) {
        std::cout << num << " ";
    }
    std::cout << std::endl;
    return 0;
}
 


 
解释:利用  bitset  ,将数组元素对应比特位置1 ,实现去重。之后遍历  bitset  ,将为1的比特位对应的整数添加到新的  vector  中,最后对这个  vector  进行排序,得到去重且有序的序列。
 

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

#include <vector>
#include <iostream>

class bitset {
public:
    bitset(size_t bitCount) : _bit((bitCount >> 5) + 1), _bitCount(bitCount) {}

    void set(size_t which) {
        if (which > _bitCount) return;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        _bit[index] |= (1 << pos);
    }

    bool test(size_t which) const {
        if (which > _bitCount) return false;
        size_t index = (which >> 5);
        size_t pos = which % 32;
        return _bit[index] & (1 << pos);
    }

    bitset intersection(const bitset& other) const {
        if (_bitCount!= other._bitCount) {
            std::cerr << "Bitsets must have the same size for intersection." << std::endl;
            return bitset(0);
        }
        bitset result(_bitCount);
        for (size_t i = 0; i < _bit.size(); ++i) {
            result._bit[i] = _bit[i] & other._bit[i];
        }
        return result;
    }

    bitset unionSet(const bitset& other) const {
        if (_bitCount!= other._bitCount) {
            std::cerr << "Bitsets must have the same size for union." << std::endl;
            return bitset(0);
        }
        bitset result(_bitCount);
        for (size_t i = 0; i < _bit.size(); ++i) {
            result._bit[i] = _bit[i] | other._bit[i];
        }
        return result;
    }

private:
    std::vector<int> _bit;
    size_t _bitCount;
};

int main() {
    // 假设集合元素的范围最大到100
    bitset setBits1(100);
    bitset setBits2(100);
    int elements1[] = {10, 20, 30};
    int elements2[] = {20, 30, 40};
    for (int element : elements1) {
        setBits1.set(element);
    }
    for (int element : elements2) {
        setBits2.set(element);
    }
    bitset intersectionResult = setBits1.intersection(setBits2);
    bitset unionResult = setBits1.unionSet(setBits2);
    std::cout << "Intersection: ";
    for (size_t i = 0; i < intersectionResult.size(); ++i) {
        if (intersectionResult.test(i)) {
            std::cout << i << " ";
        }
    }
    std::cout << std::endl;
    std::cout << "Union: ";
    for (size_t i = 0; i < unionResult.size(); ++i) {
        if (unionResult.test(i)) {
            std::cout << i << " ";
        }
    }
    std::cout << std::endl;
    return 0;
}
 


 
解释:先分别创建表示两个集合的  bitset  对象,将集合元素对应比特位置1 。通过  intersection  函数实现按位与操作得到交集位图,通过  unionSet  函数实现按位或操作得到并集位图,最后遍历位图输出交集和并集中的元素。
 

4. 操作系统中磁盘块标记
 

#include <vector>
#include <iostream>

class DiskBlockBitset {
public:
    DiskBlockBitset(size_t blockCount) : _bit((blockCount >> 5) + 1), _blockCount(blockCount) {}

    void markUsed(size_t blockIndex) {
        if (blockIndex > _blockCount) return;
        size_t index = (blockIndex >> 5);
        size_t pos = blockIndex % 32;
        _bit[index] |= (1 << pos);
    }

    void markFree(size_t blockIndex) {
        if (blockIndex > _blockCount) return;
        size_t index = (blockIndex >> 5);
        size_t pos = blockIndex % 32;
        _bit[index] &= ~(1 << pos);
    }

    bool isBlockUsed(size_t blockIndex) const {
        if (blockIndex > _blockCount) return false;
        size_t index = (blockIndex >> 5);
        size_t pos = blockIndex % 32;
        return _bit[index] & (1 << pos);
    }

private:
    std::vector<int> _bit;
    size_t _blockCount;
};

int main() {
    // 假设磁盘块总数为100
    DiskBlockBitset diskBlocks(100);
    // 标记一些磁盘块为已使用
    int usedBlocks[] = {10, 20, 30};
    for (int block : usedBlocks) {
        diskBlocks.markUsed(block);
    }
    // 检查某个磁盘块状态
    int checkBlock = 20;
    if (diskBlocks.isBlockUsed(checkBlock)) {
        std::cout << "Disk block " << checkBlock << " is used." << std::endl;
    } else {
        std::cout << "Disk block " << checkBlock << " is free." << std::endl;
    }
    // 释放一个磁盘块
    diskBlocks.markFree(20);
    if (diskBlocks.isBlockUsed(checkBlock)) {
        std::cout << "Disk block " << checkBlock << " is still used." << std::endl;
    } else {
        std::cout << "Disk block " << checkBlock << " is now free." << std::endl;
    }
    return 0;
}


 
 
解释:定义  DiskBlockBitset  类模拟操作系统中磁盘块标记。通过  markUsed  函数将指定磁盘块对应比特位置1表示已使用, markFree  函数将对应比特位置0表示空闲, isBlockUsed  函数用于检测磁盘块是否被使用。


 

四、总结
 

位图作为一种高效的数据结构,在处理海量数据的存在性判断、数据统计等方面有着广泛的应用。通过合理利用位图的特性,我们可以在很多场景下优化程序的性能,减少时间和空间复杂度。希望通过这篇博客,大家能对位图有更深入的理解,并在实际编程中灵活运用。

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

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

相关文章

猫番阅读APP:丰富资源,优质体验,满足你的阅读需求

猫番阅读APP是一款专为书籍爱好者设计的移动阅读应用&#xff0c;致力于提供丰富的阅读体验和多样化的书籍资源。它不仅涵盖了小说、非虚构、杂志等多个领域的电子书&#xff0c;还提供了个性化推荐、书架管理、离线下载等功能&#xff0c;满足不同读者的阅读需求。无论是通勤路…

MetaMask安装及使用-使用水龙头获取测试币的坑?

常见的异常有&#xff1a; 1.unable to request drip, please try again later. 2.You must hold at least 1 LINK on Ethereum Mainnet to request native tokens. 3.The address provided does not have sufficient historical activity or balance on the Ethereum Mainne…

AI:OpenAI论坛分享—《AI重塑未来:技术、经济与战略》

AI&#xff1a;OpenAI论坛分享—《AI重塑未来&#xff1a;技术、经济与战略》 导读&#xff1a;2025年4月24日&#xff0c;OpenAI论坛全面探讨了 AI 的发展趋势、技术范式、地缘政治影响以及对经济和社会的广泛影响。强调了 AI 的通用性、可扩展性和高级推理能力&#xff0c;以…

Linux配置vimplus

配置vimplus CentOS的配置方案很简单&#xff0c;但是Ubuntu的解决方案网上也很多但是有效的很少&#xff0c;尤其是22和24的解决方案&#xff0c;在此我整理了一下我遇到的问题解决方法 CentOS7 一键配置VimForCPP 基本上不会有什么特别难解决的报错 sudo yum install vims…

服务端HttpServletRequest、HttpServletResponse、HttpSession

一、概述 在JavaWeb 开发中&#xff0c;获取客户端传递的参数至关重要。http请求是客户端向服务端发起数据传输协议&#xff0c;主要包含包含请求行、请求头、空行和请求体四个部分&#xff0c;在这四部分中分别携带客户端传递到服务端的数据。常见的http请求方式有get、post、…

实验九视图索引

设计性实验 1. 创建视图V_A包括学号&#xff0c;姓名&#xff0c;性别&#xff0c;课程号&#xff0c;课程名、成绩&#xff1b; 一个语句把学号103 课程号3-105 的姓名改为陆君茹1&#xff0c;性别为女 &#xff0c;然后查看学生表的信息变化&#xff0c;再把上述数据改为原…

git 本地提交后修改注释

dos命令行进入目录&#xff0c;idea可以点击Terminal 进入命令行 git commit --amend -m "修改内容"

面向具身智能的视觉-语言-动作模型(VLA)综述

具身智能被广泛认为是通用人工智能&#xff08;AGI&#xff09;的关键要素&#xff0c;因为它涉及控制具身智能体在物理世界中执行任务。在大语言模型和视觉语言模型成功的基础上&#xff0c;一种新的多模态模型——视觉语言动作模型&#xff08;VLA&#xff09;已经出现&#…

计算机发展的历程

计算机系统的概述 一, 计算机系统的定义 计算机系统的概念 计算机系统 硬件 软件 硬件的概念 计算机的实体, 如主机, 外设等 计算机系统的物理基础 决定了计算机系统的天花板瓶颈 软件的概念 由具有各类特殊功能的程序组成 决定了把硬件的性能发挥到什么程度 软件的分类…

深度学习驱动下的目标检测技术:原理、算法与应用创新(三)

五、基于深度学习的目标检测代码实现 5.1 开发环境搭建 开发基于深度学习的目标检测项目&#xff0c;首先需要搭建合适的开发环境&#xff0c;确保所需的工具和库能够正常运行。以下将详细介绍 Python、PyTorch 等关键开发工具和库的安装与配置过程。 Python 是一种广泛应用于…

jenkins流水线常规配置教程!

Jenkins流水线是在工作中实现CI/CD常用的工具。以下是一些我在工作和学习中总结出来常用的一些流水线配置&#xff1a;变量需要加双引号括起来 "${main}" 一 引用无账号的凭据 使用变量方式引用&#xff0c;这种方式只适合只由密码&#xff0c;没有用户名的凭证。例…

基于OpenCV的SIFT特征和FLANN匹配器的指纹认证

文章目录 引言一、概述二、代码解析1. 图像显示函数2. 核心认证函数2.1 创建SIFT特征提取器2.2 检测关键点和计算描述符&#xff08;源图像&#xff09;2.3 检测关键点和计算描述符&#xff08;模板图像&#xff09;2.4 创建FLANN匹配器2.5 使用K近邻匹配 3. 匹配点筛选4. 认证…

leetcode:58. 最后一个单词的长度(python3解法)

难度&#xff1a;简单 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hello World"…

虹科应用 | 探索PCAN卡与医疗机器人的革命性结合

随着医疗技术的不断进步&#xff0c;医疗机器人在提高手术精度、减少感染风险以及提升患者护理质量方面发挥着越来越重要的作用。医疗机器人的精确操作依赖于稳定且高效的数据通信系统&#xff0c;虹科提供的PCAN四通道mini PCIe转CAN FD卡&#xff0c;正是为了满足这一需求而设…

entity线段材质设置

在cesium中,我们可以改变其entity线段材质,这里以直线为例. 首先我们先创建一条直线 const redLine viewer.entities.add({polyline: {positions: Cesium.Cartesian3.fromDegreesArray([-75,35,-125,35,]),width: 5,material:material, 保存后可看到在地图上创建了一条线段…

[STM32] 5-1 时钟树(上)

文章目录 前言5-1 时钟树&#xff08;上&#xff09;时钟树的基本介绍时钟树的基本结构大树和小树频率运算简介计数器和分频STM32内部结构树的结构于关键节点SYSCLK(System Clock) 系统时钟 72M maxHCLK(AHB Clock) AHB时钟 36M maxPLCK(APB1 Clock) APB1时钟 36M maxPLCK2(APB…

【Linux网络与网络编程】12.NAT技术内网穿透代理服务

1. NAT技术 之前我们说到过 IPv4 协议中IP 地址数量不充足的问题可以使用 NAT 技术来解决。还提到过本地主机向公网中的一个服务器发起了一个网络请求&#xff0c;服务器是怎么将应答返回到该本地主机呢&#xff1f;&#xff08;如何进行内网转发&#xff1f;&#xff09; 这就…

从辅助到协作:GitHub Copilot的进化之路

如果说现代程序员的标配工具除了VS Code、Stack Overflow之外&#xff0c;还有谁能入选&#xff0c;那一定是GitHub Copilot。从2021年首次亮相&#xff0c;到如今深度集成进开发者日常流程&#xff0c;这个“AI编程助手”已经不只是写几行自动补全代码的小帮手了&#xff0c;而…

Linux运行时的参数、命令、网络、磁盘参数和日志监控

一、监控 1. free 功能&#xff1a;用于查看系统内存使用情况&#xff0c;包括物理内存总量、已用内存、空闲内存、缓冲区&#xff08;buffer&#xff09;和缓存&#xff08;cache&#xff09;占用&#xff0c;以及交换内存&#xff08;swap&#xff09;的使用与剩余情况。常…

鸿蒙页面布局入门

本文以仿猫眼电影M站首页布局为案例&#xff0c;展示ArkUI在实际开发中的应用。内容包括案例效果及相关知识点&#xff0c;深入解析布局框架以及头部、脚部、内容区域的构建思路与代码实现&#xff0c;最后提供完整代码和教程资源&#xff0c;助力你强化实践能力。 1. 案例效果…