GitHub链接:algorithmzuo (左程云) · GitHub
第一部分:
package class032;
import java.util.HashSet;
// 位图的实现
// Bitset(int size)
// void add(int num)
// void remove(int num)
// void reverse(int num)
// boolean contains(int num)
public class Code01_Bitset {
// 位图的实现
// 使用时num不要超过初始化的大小
public static class Bitset {
public int[] set;
// n个数字 : 0~n-1
public Bitset(int n) {
// a/b如果结果想向上取整,可以写成 : (a+b-1)/b
// 前提是a和b都是非负数
set = new int[(n + 31) / 32];
}
public void add(int num) {
set[num / 32] |= 1 << (num % 32);
}
public void remove(int num) {
set[num / 32] &= ~(1 << (num % 32));
}
public void reverse(int num) {
set[num / 32] ^= 1 << (num % 32);
}
public boolean contains(int num) {
return ((set[num / 32] >> (num % 32)) & 1) == 1;
}
}
// 对数器测试
public static void main(String[] args) {
int n = 1000;
int testTimes = 10000;
System.out.println("测试开始");
// 实现的位图结构
Bitset bitSet = new Bitset(n);
// 直接用HashSet做对比测试
HashSet<Integer> hashSet = new HashSet<>();
System.out.println("调用阶段开始");
for (int i = 0; i < testTimes; i++) {
double decide = Math.random();
// number -> 0 ~ n-1,等概率得到
int number = (int) (Math.random() * n);
if (decide < 0.333) {
bitSet.add(number);
hashSet.add(number);
} else if (decide < 0.666) {
bitSet.remove(number);
hashSet.remove(number);
} else {
bitSet.reverse(number);
if (hashSet.contains(number)) {
hashSet.remove(number);
} else {
hashSet.add(number);
}
}
}
System.out.println("调用阶段结束");
System.out.println("验证阶段开始");
for (int i = 0; i < n; i++) {
if (bitSet.contains(i) != hashSet.contains(i)) {
System.out.println("出错了!");
}
}
System.out.println("验证阶段结束");
System.out.println("测试结束");
}
}
第二部分:
巧妙地reverse,每一块都要考虑reverse,面向对象的设计方法,利用变量来记录就不需要遍历查找了
package class032;
// 位图的实现
// Bitset是一种能以紧凑形式存储位的数据结构
// Bitset(int n) : 初始化n个位,所有位都是0
// void fix(int i) : 将下标i的位上的值更新为1
// void unfix(int i) : 将下标i的位上的值更新为0
// void flip() : 翻转所有位的值
// boolean all() : 是否所有位都是1
// boolean one() : 是否至少有一位是1
// int count() : 返回所有位中1的数量
// String toString() : 返回所有位的状态
public class Code02_DesignBitsetTest {
// 测试链接 : https://leetcode-cn.com/problems/design-bitset/
class Bitset {
private int[] set;
private final int size;
private int zeros;
private int ones;
private boolean reverse;
public Bitset(int n) {
set = new int[(n + 31) / 32];
size = n;
zeros = n;
ones = 0;
reverse = false;
}
// 把i这个数字加入到位图
public void fix(int i) {
int index = i / 32;
int bit = i % 32;
if (!reverse) {
// 位图所有位的状态,维持原始含义
// 0 : 不存在
// 1 : 存在
if ((set[index] & (1 << bit)) == 0) {
zeros--;
ones++;
set[index] |= (1 << bit);
}
} else {
// 位图所有位的状态,翻转了
// 0 : 存在
// 1 : 不存在
if ((set[index] & (1 << bit)) != 0) {
zeros--;
ones++;
set[index] ^= (1 << bit);
}
}
}
// 把i这个数字从位图中移除
public void unfix(int i) {
int index = i / 32;
int bit = i % 32;
if (!reverse) {
if ((set[index] & (1 << bit)) != 0) {
ones--;
zeros++;
set[index] ^= (1 << bit);
}
} else {
if ((set[index] & (1 << bit)) == 0) {
ones--;
zeros++;
set[index] |= (1 << bit);
}
}
}
public void flip() {
reverse = !reverse;
int tmp = zeros;
zeros = ones;
ones = tmp;
}
public boolean all() {
return ones == size;
}
public boolean one() {
return ones > 0;
}
public int count() {
return ones;
}
public String toString() {
StringBuilder builder = new StringBuilder();
for (int i = 0, k = 0, number, status; i < size; k++) {
number = set[k];
for (int j = 0; j < 32 && i < size; j++, i++) {
status = (number >> j) & 1;
status ^= reverse ? 1 : 0;
builder.append(status);
}
}
return builder.toString();
}
}
}
C++中的bitset详解及经典案例
bitset
是C++标准库中的一个模板类,用于处理固定大小的位序列。它提供了一种高效的方式来存储和操作二进制数据。
bitset基础
基本特性
- 固定大小:在编译时确定大小
- 高效存储:每个元素只占1位
- 丰富操作:支持位运算、访问、设置等操作
头文件
#include <bitset>
声明方式
bitset<size> name; // size必须在编译时确定
常用操作
初始化
bitset<8> b1; // 默认全0
bitset<8> b2(0xfa); // 从整数初始化: 11111010
bitset<8> b3("101010");// 从字符串初始化: 00101010
访问元素
b[0] = 1; // 设置第0位
bool bit = b[1];// 获取第1位
常用成员函数
b.set(); // 所有位置1
b.set(pos); // 指定位置1
b.reset(); // 所有位置0
b.reset(pos); // 指定位置0
b.flip(); // 所有位取反
b.flip(pos); // 指定位取反
b.count(); // 返回1的个数
b.size(); // 返回总位数
b.test(pos); // 检查指定位是否为1
b.any(); // 是否有至少一个1
b.none(); // 是否全为0
b.all(); // 是否全为1
位运算
b1 & b2; // 按位与
b1 | b2; // 按位或
b1 ^ b2; // 按位异或
~b1; // 按位取反
b1 << 2; // 左移2位
b1 >> 3; // 右移3位
经典案例
案例1:统计二进制中1的个数
#include <iostream>
#include <bitset>
int main() {
unsigned int num = 25; // 11001
std::bitset<32> b(num);
std::cout << "Number of set bits in " << num << ": " << b.count() << std::endl;
return 0;
}
案例2:位掩码应用
#include <iostream>
#include <bitset>
int main() {
const int SIZE = 8;
std::bitset<SIZE> permissions("01101001");
// 检查读权限(第2位)
if (permissions.test(2)) {
std::cout << "Read permission granted" << std::endl;
}
// 设置写权限(第1位)
permissions.set(1);
std::cout << "New permissions: " << permissions << std::endl;
return 0;
}
案例3:埃拉托斯特尼筛法(素数筛)
#include <iostream>
#include <bitset>
#include <vector>
void sieveOfEratosthenes(int n) {
std::bitset<1000000> isPrime;
isPrime.set(); // 初始假设所有数都是素数
for (int p = 2; p * p <= n; p++) {
if (isPrime[p]) {
for (int i = p * p; i <= n; i += p) {
isPrime.reset(i);
}
}
}
// 输出素数
for (int p = 2; p <= n; p++) {
if (isPrime[p]) {
std::cout << p << " ";
}
}
}
int main() {
int n = 100;
sieveOfEratosthenes(n);
return 0;
}
案例4:位集合运算
#include <iostream>
#include <bitset>
int main() {
std::bitset<4> b1("1010");
std::bitset<4> b2("1100");
std::cout << "b1: " << b1 << "\n";
std::cout << "b2: " << b2 << "\n";
std::cout << "AND: " << (b1 & b2) << "\n";
std::cout << "OR: " << (b1 | b2) << "\n";
std::cout << "XOR: " << (b1 ^ b2) << "\n";
std::cout << "NOT b1: " << (~b1) << "\n";
return 0;
}
案例5:子集枚举
#include <iostream>
#include <bitset>
void printSubsets(const std::vector<int>& arr) {
int n = arr.size();
for (int mask = 0; mask < (1 << n); ++mask) {
std::bitset<32> bits(mask);
std::cout << "{ ";
for (int i = 0; i < n; ++i) {
if (bits[i]) {
std::cout << arr[i] << " ";
}
}
std::cout << "}\n";
}
}
int main() {
std::vector<int> arr = {1, 2, 3};
printSubsets(arr);
return 0;
}
性能考虑
- 空间效率:bitset比bool数组更节省空间,每个元素只占1位
- 操作效率:位运算通常比算术运算更快
- 固定大小:编译时确定大小,无法动态调整
bitset非常适合需要高效位操作的应用场景,如状态压缩、位掩码、集合运算等。