C++位图的实现与详解

1.位图概念

在讲解位图之前我们先来看一道很经典的面试题。

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

我们有以下两种种解决方法:

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

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

深入分析:解题思路2是否可行,我们先算算40亿个数据大概需要多少内存。1G=1024MB=1024*1024KB=1024*1024*1024Byte约等于10亿多Byte那么40亿个整数约等于16G,说明40亿个数是无法直接放到内存中的,只能放到硬盘文件中。而二分查找只能对内存数组中的有序数据进行查找。

其实我们并不需要开如此大的空间,我们只需要知道这个数据在或者不在,刚好是两种状态,那么可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。

位图概念

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

2.位图的实现

cpp 复制代码
namespace bit
{
	template<size_t N>
	class bitset
	{
	public:

	private:
		std::vector<int> _bs;
	};
}

现在我们要做的是如何修改x 对应的位置比特位。(如下)

i = x / 32 得到 x在vector中的第 i 个整形数据。

a = x % 32 得到 x在第 i 位的第 a位。

cpp 复制代码
namespace bit
{
	template<size_t N>
	class bitset
	{
	public:
		bitset()
		{
			_bs.resize(N / 32 + 1);
		}

		void set(size_t x)//表示x 存在
		{
			int i = x / 32;
			int a = x % 32;
            
			_bs[i] |= (1 << a);
		}

		void reset(size_t x)//表示x 不存在
		{
			int i = x / 32;
			int a = x % 32;

			_bs[i] &= (~(1 << a));
		}

		//x处映射的值是1 返回真
		//x处映射的值是0 返回假
		bool rest(size_t x)
		{
			int i = x / 32;
			int a = x % 32;

			return _bs[i] & (1 << a);
		}

	private:
		std::vector<int> _bs;
	};
}
cpp 复制代码
int main()
{
	bit::bitset<100> bs;
	bs.set(32);
	bs.reset(32);
	bs.set(33);
	cout<<bs.rest(31)<<endl;
	cout<<bs.rest(32)<<endl;
	cout<<bs.rest(33)<<endl;
	cout<<bs.rest(34)<<endl;
	cout<<bs.rest(35)<<endl;

	return 0;
}

测试结果如下:

3.bitset的使用

如果要开大量空间我们可以这样

但是库里面是开不出来的

因为我们自己实现的底层使用的是vector 开空间是在堆上面,而库里面底层实现是静态数组,所以开不出来。

可以看到我们无论开多大的空间他本身所占的内存都是16因为我们的核心空间都开在堆上的。

库里面就不一样了如下:

大家使用库里面bitset的就要小心使用。

解决办法如下:

位图的优缺点:

  • 优点:增删查改快,节省空间
  • 缺点:只能适用整形

接下来看几道关于位图的题目:

  • 给定40亿个整数,设计算法找到只出现一次的整数。

我们可以用

00:表示0次

01:表示1次

10:表示2次及以上

cpp 复制代码
void testbitset(vector<int>& arr)
{
	bit::bitset<-1> bs1;
	bit::bitset<-1> bs2;
	for (auto& e : arr)
	{
		if (!bs1.rest(e) && !bs2.rest(e))//00->01
		{
			bs2.set(e);
		}
		else if(!bs1.rest(e) && bs2.rest(e))//01->10
		{
			bs2.reset(e);
			bs1.set(e);//10
		}
		else//10->11
		{
			bs2.set(e);
		}
	}

	for (int e = 0; e < 100; e++)
	{
		if (!bs1.rest(e) && bs2.rest(e))
		{
			//出现一次
			cout << "1->:" << e << endl;
		}
		else if (bs1.rest(e) && !bs2.rest(e))
		{
			//出现两次
			cout << "2->:" << e << endl;
		}
		else if(bs1.rest(e) && bs2.rest(e))
		{
			//出现三次及以上
			cout << "3->:" << e << endl;
		}
	}
}



int main()
{
	vector<int> v1({0, 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 5, 6, 6, 6, 6});
	testbitset(v1);
}

感谢大家的观看!

相关推荐
神仙别闹2 分钟前
基本MFC类框架的俄罗斯方块游戏
c++·游戏·mfc
娅娅梨1 小时前
C++ 错题本--not found for architecture x86_64 问题
开发语言·c++
兵哥工控1 小时前
MFC工控项目实例二十九主对话框调用子对话框设定参数值
c++·mfc
我爱工作&工作love我1 小时前
1435:【例题3】曲线 一本通 代替三分
c++·算法
娃娃丢没有坏心思1 小时前
C++20 概念与约束(2)—— 初识概念与约束
c语言·c++·现代c++
lexusv8ls600h1 小时前
探索 C++20:C++ 的新纪元
c++·c++20
lexusv8ls600h2 小时前
C++20 中最优雅的那个小特性 - Ranges
c++·c++20
白-胖-子2 小时前
【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-统计数字
开发语言·c++·算法·蓝桥杯·等考·13级
好睡凯2 小时前
c++写一个死锁并且自己解锁
开发语言·c++·算法
依旧阳光的老码农2 小时前
标准C++ 字符串
开发语言·c++