BitMap(位图)是一种用于存储和操作二进制位(0和1)的数据结构。它将每个元素表示为一个二进制位,可以使用位运算对位图进行高效的插入、删除和查询操作。
BitMap常用于以下场景:
- 去重:可以使用BitMap来判断某个元素是否存在,例如判断一个数字是否在一个大数据集中出现过。
- 排序:可以使用BitMap来对一组数字进行排序,通过设置对应位置的位来表示数字的出现与否,然后按位进行遍历得到排序结果。
- 压缩存储:当需要存储大量的布尔类型数据时,使用BitMap可以大幅减少存储空间,因为每个位只占用1比特。
在使用BitMap时,通常需要将数据映射到BitMap中的位位置。例如,对于一个整数集合,可以将每个整数映射到BitMap中的对应位上。设置位为1表示该整数存在,位为0表示该整数不存在。
以下是使用BitMap的基本操作:
- 设置位(Set):将指定位置的位设置为1。
- 清除位(Clear):将指定位置的位设置为0。
- 查询位(Get):获取指定位置的位的值(0或1)。
- 取反位(Flip):将指定位置的位的值取反(0变为1,1变为0)。
- 统计位(Count):计算BitMap中值为1的位的个数。
- 按位与(AND):将两个BitMap进行按位与操作。
- 按位或(OR):将两个BitMap进行按位或操作。
- 按位异或(XOR):将两个BitMap进行按位异或操作。
需要注意的是,BitMap适用于数据集较小且值范围可控的情况,因为BitMap的大小直接取决于数据集的最大值。当数据集较大时,BitMap可能会占用大量的内存。
在Go语言中,可以使用内置的位运算操作符和数据类型(如uint64
)来实现BitMap。通过将整数映射到BitMap的位上,可以进行高效的位操作。具体实现可以根据具体需求进行设计。
package main
import "fmt"
func main() {
// 假设需要表示的数据范围为 0 到 99
maxValue := 100
// 创建一个长度为 (maxValue/8)+1 的切片用于存储 BitMap
bitmap := make([]byte, (maxValue/8)+1)
// 设置位
SetBit(bitmap, 5)
SetBit(bitmap, 10)
SetBit(bitmap, 20)
// 查询位
fmt.Println(GetBit(bitmap, 5)) // 输出: true
fmt.Println(GetBit(bitmap, 10)) // 输出: true
fmt.Println(GetBit(bitmap, 15)) // 输出: false
// 清除位
ClearBit(bitmap, 10)
// 查询位
fmt.Println(GetBit(bitmap, 10)) // 输出: false
// 输出bitmap的二进制
fmt.Printf("%08b\n", bitmap) // 输出: [00100000 00000000 00010000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000]
}
// SetBit 设置位
func SetBit(bitmap []byte, num int) {
index := num / 8
offset := num % 8
bitmap[index] |= (1 << uint(offset))
}
// ClearBit 清除位
func ClearBit(bitmap []byte, num int) {
index := num / 8
offset := num % 8
bitmap[index] &= ^(1 << uint(offset))
}
// GetBit 查询位
func GetBit(bitmap []byte, num int) bool {
index := num / 8
offset := num % 8
return (bitmap[index] & (1 << uint(offset))) != 0
}
再看一个例子
package main
import "fmt"
type BitMap []uint64
func NewBitMap(size int) BitMap {
numWords := size/64 + 1
return make(BitMap, numWords)
}
func (bitmap BitMap) Set(index int) {
wordIndex := index / 64
bitIndex := index % 64
bitmap[wordIndex] |= 1 << bitIndex
}
func (bitmap BitMap) Clear(index int) {
wordIndex := index / 64
bitIndex := index % 64
bitmap[wordIndex] &^= 1 << bitIndex
}
func (bitmap BitMap) Get(index int) bool {
wordIndex := index / 64
bitIndex := index % 64
return (bitmap[wordIndex] & (1 << bitIndex)) != 0
}
func (bitmap BitMap) Flip(index int) {
wordIndex := index / 64
bitIndex := index % 64
bitmap[wordIndex] ^= 1 << bitIndex
}
func (bitmap BitMap) Count() int {
count := 0
for _, word := range bitmap {
for word != 0 {
count += int(word & 1)
word >>= 1
}
}
return count
}
func (bitmap BitMap) And(other BitMap) BitMap {
result := make(BitMap, len(bitmap))
for i := range bitmap {
result[i] = bitmap[i] & other[i]
}
return result
}
func (bitmap BitMap) Or(other BitMap) BitMap {
result := make(BitMap, len(bitmap))
for i := range bitmap {
result[i] = bitmap[i] | other[i]
}
return result
}
func (bitmap BitMap) Xor(other BitMap) BitMap {
result := make(BitMap, len(bitmap))
for i := range bitmap {
result[i] = bitmap[i] ^ other[i]
}
return result
}
func main() {
bitmap := NewBitMap(128)
// 设置位
bitmap.Set(5)
bitmap.Set(10)
bitmap.Set(20)
// 清除位
bitmap.Clear(10)
// 查询位
fmt.Println("位 5 的值:", bitmap.Get(5))
fmt.Println("位 10 的值:", bitmap.Get(10))
fmt.Println("位 20 的值:", bitmap.Get(20))
// 取反位
bitmap.Flip(5)
bitmap.Flip(15)
// 统计位
count := bitmap.Count()
fmt.Println("位为 1 的个数:", count)
// 创建另一个 BitMap
otherBitmap := NewBitMap(128)
otherBitmap.Set(10)
otherBitmap.Set(15)
// 按位与
andResult := bitmap.And(otherBitmap)
fmt.Println("按位与结果:", andResult)
// 按位或
orResult := bitmap.Or(otherBitmap)
fmt.Println("按位或结果:", orResult)
// 按位异或
xorResult := bitmap.Xor(otherBitmap)
fmt.Println("按位异或结果:", xorResult)
}
结果为
位 5 的值: true
位 10 的值: false
位 20 的值: true
位为 1 的个数: 2
按位与结果: [32768 0 0]
按位或结果: [1082368 0 0]
按位异或结果: [1049600 0 0]