【 C++ 】哈希的应用(1)bitset位图

目录

1、bitset的介绍

        位图的引入

        位图的概念

        位图的应用

2、bitset的使用

        bitset的构造方式

        bitset成员函数的使用

        bitset运算符的使用


1、bitset的介绍

位图的引入

看这样一道面试题:

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

单纯从判断一个数是否在一串数字的角度看,我们很容易想到下面的方法:

  1. 把这40亿个整数放到set、unordered_set容器里头,调用find函数来判断
  2. 把这40亿个整数进行外排序,再去二分查找

单就此题而言,为了推翻上面两种方法,首先,我们要清楚40亿个整数,占用多少空间:

40亿个整数 = 160亿个字节
1GB = 1024MB
1024MB = 1024 * 1024KB
1024 * 1024KB = 1024 * 1024 * 1024Byte ≈ 10亿字节
综上1GB ≈ 10亿字节
   16GB ≈ 160亿字节
  • 计算得知,40亿整数占16个G内存,光数据就占了16G,若放到set容器,其底层红黑树的内部也有负载的消耗(存颜色,三叉连……),再算上16G的消耗,消耗太大了,内存不够,承受不住。同理,内存不够,数据压根放不到内存,也就不能进行排序。

为了解决此问题,这就需要我们用到位图来解决。

  • 我们在判断一个数据是否在给定的整形数据中,结果只有在或者不在这两种状态,那么就可以使用一个二进制比特位来代表数据是否存在的信息,如果二进制比特位为1,代表存在,为0代表不存在。这里我们采用直接定址法的哈希,用一个比特位标识映射值在不在,这就是位图。示例:

对于40亿个整数,我们要我们要开整型的最大值(2^32 - 1)个bit位,大概占500MB的内存:

1G = 2^30Byte ≈ 10亿字节
(2^32 - 1)Byte ≈ 40亿字节 ≈ 4G
1Byte = 8bit
(2^32 - 1)bit = 4G/8 ≈ 500MB

 由此可见,使用位图的方法,大大减少了内存的消耗,并且能很好的解决此问题。


位图的概念

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


位图的应用

  1. 快速查找某个数据是否在一个集合中
  2. 排序 + 去重
  3. 求两个集合的交集、并集等
  4. 操作系统中磁盘块标记

2、bitset的使用

bitset的构造方式

1、使用默认构造函数,构造一个16位的位图,默认初始化为0

bitset<16> bs1;

2、元素按照给定整数的二进制位进行初始化:0xff ——> 1111 1111

bitset<16> bs2(0xfa2);//0000111110100010

3、使用01的string进行初始化:std::string(“01101001”) ——> 01101001

bitset<16> bs3(string("01101001"));//0000000001101001

4、使用01的字符串进行初始化:(“01101001”) ——> 01101001

bitset<16> bs4("01101001");//0000000001101001

bitset成员函数的使用

bitset常用成员函数如下表格所示:

成员函数功能
set设置指定位或所有位
reset清空指定位或所有位
flip反转指定位或所有位
test获取指定位的状态
count获取被设置位的个数
size获取可以容纳的位的个数
any如果有任何一个位被设置则返回true
none如果没有位被设置则返回true
all如果所有位都被设置则返回true

示例:

int main()
{
	bitset<16> bs;
	bs.set(4);
	bs.set(6);
	bs.set(2);
	cout << bs.size() << endl;//16
	cout << bs << endl;//0000000001010100
	//获取指定位的状态
	cout << bs.test(0) << endl;//0
	cout << bs.test(2) << endl;//1
	//反转所有位
	bs.flip();
	cout << bs << endl;//1111111110101011
	//反转第1位
	bs.flip(1);
	cout << bs << endl;//1111111110101001
	cout << bs.count() << endl;//12
	//清空第3位
	bs.reset(3);
	cout << bs << endl;//1111111110100001
	//清空所有位
	bs.reset();
	cout << bs.none() << endl;//1
	cout << bs.any() << endl;//0
	//设置所有位
	bs.set();
	cout << bs.all() << endl;//1
	return 0;
}

注意: 使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位


bitset运算符的使用

如表格所示:

运算符功能说明
>>、<<输入输出运算符
=赋值运算符
==、!=关系运算符
&=、|=、^=、<<=、>>=复合赋值运算符
~单目运算符
&、|、^位运算符
[ ]operator[ ]运算符

示例:

int main()
{
	//>>输入、<<输出运算符
	bitset<8> bs;
	cin >> bs;//10100
	cout << bs << endl;//00010100
	//复合赋值运算符
	bitset<8> bs1("101011");
	bitset<8> bs2("100100");
	cout << (bs1 >>= 2) << endl;//00001010
	cout << (bs2 |= bs1) << endl;//00101110
	//位运算符
	bitset<8> bs3("10010");
	bitset<8> bs4("11001");
	cout << (bs3 & bs4) << endl;//00010000
	cout << (bs3 ^ bs4) << endl;//00001011
	//operator[]运算符
	cout << bs3[4] << endl;//1
	cout << bs3[2] << endl;//0
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三分苦

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值