1、位图的概念
位图(bitset)是一种常用的数据结构,常用在给一个很大范围的数,判断其中的一个数是不是在其中。在索引、数据压缩方面有很大的应用。
位图是用数组实现的,数组的每一个元素的每一个二进制位都表示一个数据,0表示该数据不存在,1表示该数据存在
如下图:
一般来说一个位图所占的空间为512M,所以说要判断一个数是否在0~4294967295中需要的空间是512M(即一个位图)来解决。
2、位图的实现
#pragma once
#include<iostream>
#include<vector>
using namespace std;
class bitset
{
vector<int> m_bit;
size_t m_bitcount;//bitset的总大小,相当于capacity
public:
bitset(size_t bite) :
m_bit(bite / 32 + 1),
m_bitcount(bite)
{
}
//给某一位置一
void set(size_t pos)
{
if (pos > m_bitcount)
{
return;
}
//例:表示1,22,34
//[0]:00000000 01000000 00000000 00000010
//[1]:00000000 00000000 00000000 00000100
//[2]:00000000 00000000 00000000 00000000
//[3]:00000000 00000000 00000000 00000000
size_t index = pos / 32;//表示下标
size_t bits = pos % 32;//表示每组中的位置
m_bit[index] |= 1u << bits;//置一
}
//给某一位置零
void reset(size_t pos)
{
if (pos > m_bitcount)
{
return;
}
size_t index = pos / 32;//表示下标
size_t bits = pos % 32;//表示每组中的位置
m_bit[index] &= ~(1u << bits);//置零
}
//判断某一位的结果是0或1
bool get(size_t pos)
{
if (pos > m_bitcount)
{
return;
}
size_t index = pos / 32;//表示下标
size_t bits = pos % 32;//表示每组中的位置
return m_bit[index] >> bits & 1;
}
//位图不支持扩容
size_t size()
{
return m_bitcount;
}
//数位图中一共有多少个1
size_t count()
{
const char *pCount = "\0\1\1\2\1\2\2\3\1\2\2\3\2\3\3\4";
// 0 1 2 3 4 5 6 7 8 9 a b c d e f
size_t size = m_bit.size();
size_t count = 0;
size_t i;
for (i = 0; i < size; i++)
{
int value = m_bit[i];
for (int j = 0; j < sizeof(m_bit[0]); j++, value >>= 8)
{
char c = value;//char类型对value进行截断,相当于取8位
count += pCount[c & 0x0f];//计算低4位中1的个数
c >> 4;//右移4后,计算剩下4位中1的个数
count += pCount[c & 0x0f];
}
}
}
};
3、位图的应用
- 快速查找某个数据是否在一个集合中
- 排序
- 求两个集合的交集、并集等
- 操作系统中磁盘块标记
4、例题
-
给定100亿个整数,设计算法找到只出现一次的整数?
思路:利用两个位图,当某个数出现第一次将位图1 对应的位置置1,若出现第二次或者更多次将位图2置1,最终结果若位图1的某个位置为1 ,位图2对应的位置为0时,说明该数字只出现了一次。 -
给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
思路:每个文件各用一个位图,若数字出现将对应的位置置1,最后扫描两个位图,相同位置均为1时,说明该数字为两个文件的交集。 -
1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数?
思路:利用两个位图,位图的状态进行判断:
若位图1、位图2均为0(表示该数字之前未出现),则将位图1对应的位置置1(变换后为1 0);
若位图1为1,位图2为0(表示该数字之前出现过1次),则将位图2对应的位置置1(变换后为1 1);
若位图1、位图2均为1(表示该数字在之前出现过2次),则将位图1对应的位置置0(变换后为0 1);
若位图1为0,位图2为1(表示该数字在之前出现过三次及以上),不做任何改变;
所以除了位图1为0,位图2为1对应的数字,其他数字出现的次数不超过2次。