Bitmap快速实现排序、查询、去重

Bitmap快速实现排序、查询、去重

记得很久之前看到的一个看似逼格很高的面试题:

一个存满32位整数的200G磁盘,怎样用4G内存找到所有出现过恰好3次的整数。

所有32位整数共2 ^ 32个每个又占4 byte (32 bit),所以一共需要2^32*4/1024/1024/1024 = 16G,如果单纯遍历并存储所有出现的数字,4G内存肯定是远远不够的,所以有没有什么更好更省空间的办法呢?答案是肯定的。

我们可以创建一个2 ^ 32长度的byte数组a ([2 ^ 32]byte),大小正好位2^32/1024/1024/1024 = 4G。数组的每一个元素代表一个数字及其出现次数,比如a[0]代表0出现的次数,当数字i每出现一次,a[i]就加1,如果a[i] > 3,a[i]就无需再增加,因为我们只要找出刚好出现三次的整数,超过三次的我们就可以不再关注了。

数字1出现两次
a[1]
在这里插入图片描述
数字18出现三次
a[18]
在这里插入图片描述
其实,还可以更省空间,每个数字并不需要一个byte来存储其出现次数,只要有3个bit位就够了,000未出现,001出现一次,010出现两次,011出现三次,100出现大于三次,这样总共需要2^32*3/1024/1024/1024 = 1.5G就够了。
在这里插入图片描述
下面介绍一下bitmap(位图)。

所谓的bitmap就是用一个bit位来标记某个元素所对应的value,而key就是该元素,由于bitmap使用bit来存储数据,因此可以大大节省存储空间。在位图中,每个元素位1或0,表示其对应的元素是否存在。

来看一个具体例子,假设我们要对0~7之中的5个数字进行排序,比如(4, 7, 2, 5, 3)。要表示8个数字,我们只需要开辟8 bit (1 byte)的空间,并将所有bit位设为0;然后遍历这5个数字,第一个是4,所以把下标为4(也就是第五个)的元素设为1,以此类推;最后我们遍历这个8 bit,把值为1的输出,即得到排好序的元素(2, 3, 4, 5, 7)。
在这里插入图片描述
bitmap的golang实现:

// bitmap数据结构
type Bitmap struct {
    data []byte
    bitsize uint64
}
// 置位和清位
func (this *Bitmap) SetBit(offset uint64, value uint8) bool {
    index, pos := offset/8, offset%8
    if this.bitsize < offset {
        return false
    }
    if value == 0 {
        this.data[index] &^= 0x01 << pos
    } else {
        this.data[index] |= 0x01 << pos
    }
    return true
}

典型应用场景:

  • 快速排序:上例
  • 快速去重:00表示不存在,01表示存在一个,11表示存在两个或更多
  • 快速查询:1表示存在,0表示不存在

优点:

  • 运算效率高,不需要进行比较和移位;
  • 占用内存少。

缺点:

  • 不可对重复的数据进行排序和查找。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值