C++----bitmap位图的使用

位图(Bitmap )是一种用二进制位(bit)来表示数据状态的存储结构,常用于节省内存、进行快速查找、去重或集合操作。

核心概念

位图的思想很简单:

用一个二进制位代表一个元素的"存在与否"。

  • 1 表示某个数存在;

  • 0 表示某个数不存在。

例如要存储 [1, 3, 5]

下标(表示数字) 0 1 2 3 4 5
位状态 0 1 0 1 0 1

这样我们只用了 6 位(二进制)就能表示 6 个数的存在性。


典型用途

  1. 大规模数据去重

    • 判断某个数是否出现过:O(1)

    • 内存比 unordered_setmap 小得多。

  2. 快速排序辅助

    • 只需扫描位图的每一位,即可得到排序结果(天然有序)。
  3. 布隆过滤器(Bloom Filter)底层实现

    • 位图配合多个哈希函数实现高效的"近似存在"判断。
  4. 权限管理 / 状态压缩

    • 用每一位代表某种状态或权限标记。
优点 缺点
占用内存极小 只能表示"是否存在",不能存储实际值
查找速度极快 只能用于整数或能映射成整数的值
可用于排序和去重 数据范围过大时依然可能超内存(如 10¹² 级别)

面试题

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

第一个思路,遍历,虽然复杂度是O(N),但40亿不是小数目;第二种用红黑树,set解决,虽然他的插入和搜索很快,O(logN),但是建树很慢,约等于N*O(logN),也很大了;第三种排序+二分搜索,虽然搜索可以做到O(logN),但是排序最少也是O(N*logN),还是比较大的;同时,就算可以用上述的方式做到,40亿个整型数据,大概要占4000000000 个 int×4 字节=16000000000 字节÷1024÷1024÷1024≈14.9GB,试问一般的计算机内存大都是8GB,也有16GB的,但是总不能把空间都开给这个程序吧。所以这里就用到位图,可以缩小到八分之一,减小内存的消耗。

模拟实现,这里用到了位运算,大家可以自己动手理一理。通过对存放int的vector进行处理,set就是置1,reset置0,test检查是否存在X这个数字。这里还用到了非类型模板参数,在编译时可以确定参数的大小。注意resize中的数字正是为vector中存储的数据所开辟的个数,所以这里要注意,传入的N是比特位数,除以32就是int的数量。

cpp 复制代码
	template<size_t N>
	class bitmap
	{
	public:

		bitmap()
		{
			_bm.resize(N/32+1,0);
		}

		void set(int x)
		{
			int i = x / 32;
			int j = x % 32;
			_bm[i] |= 1 << j;
		}

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

		bool test(int x)
		{
			int i = x / 32;
			int j = x % 32;
			return _bm[i] & (1 << j);
		}
	private:
		vector<int> _bm;
	};

2.给定100亿个整数,设计算法找到只出现一次的整数?1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数。

这个题可以用两个位图来处理,我们先假设为每个值分配两个比特位,00代表不存在,10代表出现一次,01代表出现两次,11代表出现两次以上。那么是否要使用连续的两个比特位呢?不太好处理,那就用现成的位图:这个处理的是第一个问题,第二个问题可以扩展,请读者自行完成

cpp 复制代码
	template<size_t N>
	class two_bitmap
	{
	public:
		void set(int x)
		{
			//00一次也未出现
			if (!_bm1.test(x) && !_bm2.test(x))
			{
				_bm1.set(x);
			}
			//10出现了一次 设置成01
			else if (_bm1.test(x) && !_bm2.test(x))
			{
				_bm1.reset(x);
				_bm2.set(x);
			}
            //两次及两次以上
			else
				;
		}

		bool is_once(int x)
		{
			return _bm1.test(x) && !_bm2.test(x);
		}

	private:
		bitmap<N> _bm1;
		bitmap<N> _bm2;
	};

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

要考虑到两个文件会中各自会有重复的文件,所以将两个文件考虑合并成一个文件,再用上面题2的处理方式是不行的。那么可以各自对应一个位图进行处理,数据存在的对应位置设置成1,不必理会是否重复。然后再对各自的位图进行检查:

cpp 复制代码
	//假设a和b是两个不同的文件
    int a[] = { 1,2,3,4,5,6,7,1 };
	int b[] = { 2,4,6,8,3,1,0,8,9,4 };

	wzz::bitmap<10> b1;
	wzz::bitmap<10> b2;
	for (auto e : a)
	{
		b1.set(e);
	}

	for (auto e : b)
	{
		b2.set(e);
	}

    //利用set去重
	wzz::unorder_set<int> s1;
	for (auto e : a)
	{
		if (b2.test(e))
			s1.insert(e);
	}
	cout << "交叉文件有:" << endl;
	for (auto e : s1)
	{
		cout << e << " ";
	}
相关推荐
BingeBlog3 小时前
[01] Qt的UI框架选择和对比
开发语言·c++·笔记·qt·ui·开源软件
小许学java3 小时前
Spring AI快速入门以及项目的创建
java·开发语言·人工智能·后端·spring·ai编程·spring ai
AGG_Chan3 小时前
flutter专栏--深入了解widget原理
开发语言·javascript·flutter
greentea_20133 小时前
Codeforces Round 173 B. Digits(2043)
c++·算法
Darenm1114 小时前
JavaScript事件流:冒泡与捕获的深度解析
开发语言·前端·javascript
whltaoin4 小时前
Java 后端与 AI 融合:技术路径、实战案例与未来趋势
java·开发语言·人工智能·编程思想·ai生态
wjs20244 小时前
jEasyUI 自定义窗口工具栏
开发语言
二十雨辰4 小时前
vite与ts的结合
开发语言·前端·vue.js
亦良Cool4 小时前
如何部署一个Java项目
java·开发语言