C++STL(九) :bitset的介绍与使用
文章目录
- C++STL(九) :bitset的介绍与使用
- bitset的介绍
- 位图的引入
- 位图的概念
- 位图的应用
- 优缺点分析
- bitset的使用
- 定义方式
- 成员函数的使用
- 运算符的使用
C++STL(九) :bitset的介绍与使用
bitset的介绍
位图的引入
假设面试官考了你一道题:给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中?
要判断一个数是否在某一堆数中,我们可能会想到如下方法:
- 将这一堆数进行排序,然后通过二分查找的方法判断该数是否在这一堆数中
- 将这一堆数插入到unordered_set容器中,然后调用find函数判断该数是否在这一堆数中
单从时间复杂度上来看,这两种方法都是可以,而且效率也不错,第一种方法的时间复杂度是O(NlogN),第二种方法的时间复杂度是O(N)
但问题是这里有40亿个数,若是我们要将这些数全部加载到内存当中,那么将会占用16G的空间,空间消耗是很大的。因此从空间消耗来看,上面这两种方法实际都是不可行的
此时我们就可以使用位图来解决这个问题
在这个问题当中,我们只需要判断一个数在或是不在,即只有两种状态,那么我们可以用一个比特位来表示数据是否存在,如果比特位为1则表示存在,比特位为0则表示不存在。比如:
无符号整数总共有232个,因此记录这些数字就需要232个比特位,也就是512M的内存空间,内存消耗大大减少,使得解决这个问题成为可能
位图的概念
位图(Bitmap)是一种基于哈希思想的高效数据结构,通过比特位(bit)的0/1状态来记录数据的存在性。其核心设计目标是以极低的内存消耗处理海量无重复数据的快速查询,尤其在处理整数集合的成员判定问题时具有显著优势
位图的应用
- 海量数据查询
- 经典面试题:快速判断某数是否在40亿无符号整数中,位图可在512MB内存内实现O(1)查询
- 实际应用:
- 用户历史记录去重(如视频观看记录)
- 网络爬虫URL去重
- 数据统计与集合运算
- 排序去重:遍历位图输出所有置1的位,即有序无重复集合
- 交并集运算:通过按位与(&)和按位或(|)快速计算集合的交集和并集
- 系统级应用
- 磁盘块管理:标记空闲块(0)与已用块(1),快速分配与回收
- 进程内存管理:跟踪内存页使用状态
优缺点分析
优点 | 局限性 |
---|---|
极低内存占用(空间复杂度O(N/8)) | 仅适用于整型数据,字符串需额外哈希处理 |
常数级查询效率(O(1)) | 无法处理重复数据 |
支持高效集合运算(与、或、非) | 数据范围过大时仍需较大内存(如N=10^18) |
无哈希冲突问题 | 删除操作需谨慎(可能影响其他数据) |
bitset的使用
定义方式
- 方式一: 构造一个16位的位图,所有位都初始化为0
bitset<16> bs1; //0000000000000000
- 方式二: 构造一个16位的位图,根据所给值初始化位图的前n位
bitset<16> bs2(0xfa5); //0000111110100101
- 方式三: 构造一个16位的位图,根据字符串中的0/1序列初始化位图的前n位
bitset<16> bs3(string("10111001")); //0000000010111001
成员函数的使用
成员函数 | 功能 |
---|---|
set | 设置指定位或所有位 |
reset | 清空指定位或所有位 |
flip | 反转指定位或所有位 |
test | 获取指定位的状态 |
count | 获取被设置位的个数 |
size | 获取可以容纳的位的个数 |
none | 如果有任何一个位被设置则返回true |
any | 如果没有位被设置则返回true |
all | 如果所有位都被设置则返回true |
#include <iostream>
#include <bitset>
using namespace std;int main()
{bitset<8> bs;bs.set(2); //设置第2位bs.set(4); //设置第4位cout << bs << endl; //00010100bs.flip(); //反转所有位cout << bs << endl; //11101011cout << bs.count() << endl; //6cout << bs.test(3) << endl; //1bs.reset(0); //清空第0位cout << bs << endl; //11101010bs.flip(7); //反转第7位cout << bs << endl; //01101010cout << bs.size() << endl; //8cout << bs.any() << endl; //1bs.reset(); //清空所有位cout << bs.none() << endl; //1bs.set(); //设置所有位cout << bs.all() << endl; //1return 0;
}
使用成员函数set、reset、flip时,若指定了某一位则操作该位,若未指定位则操作所有位
运算符的使用
- bitset中>>、<<运算符的使用
#include <iostream>
#include <bitset>
using namespace std;int main()
{bitset<8> bs;cin >> bs; //10110cout << bs << endl; //00010110return 0;
}
- bitset中赋值运算符、关系运算符、复合赋值运算符、单目运算符的使用
#include <iostream>
#include <string>
#include <bitset>
using namespace std;int main()
{bitset<8> bs1(string("10101010"));bitset<8> bs2(string("10101010"));bs1 >>= 1;cout << bs1 << endl; //01010101bs2 |= bs1;cout << bs2 << endl; //11111111return 0;
}
- bitset中位运算符的使用
#include <iostream>
#include <string>
#include <bitset>
using namespace std;int main()
{bitset<8> bs1(string("10101010"));bitset<8> bs2(string("01010101"));cout << (bs1&bs2) << endl; //00000000cout << (bs1|bs2) << endl; //11111111cout << (bs1^bs2) << endl; //11111111return 0;
}
- bitset中[ ]运算符的使用
#include <iostream>
#include <string>
#include <bitset>
using namespace std;int main()
{bitset<8> bs(string("00110101"));cout << bs[0] << endl; //1bs[0] = 0;cout << bs << endl; //00110100return 0;
}#include <string>
#include <bitset>
using namespace std;int main()
{bitset<8> bs(string("00110101"));cout << bs[0] << endl; //1bs[0] = 0;cout << bs << endl; //00110100return 0;
}