什么是位图?

bit:计算机最小单位(0或者1),逻辑上可以表示是/否,有/无等,位图是一种用“二进制位(bit)”来表示状态的数据结构

位图的核心能力是省内存 + 查询快(O(1)),表示N个值的状态只需要N个bit,内存使用是N/8个字节,并且是一个Map,查询

应用场景

快速去重

输入:[7, 3, 7, 2, 3, 9](范围 0~9)

过程:

  • 看到 7:第 7 位从 0->1,输出 7

  • 看到 3:第 3 位从 0->1,输出 3

  • 再看到 7:第 7 位已是 1,跳过

  • 看到 2:第 2 位从 0->1,输出 2

  • 再看到 3:跳过

  • 看到 9:第 9 位从 0->1,输出 9

结果:[7, 3, 2, 9]
这比 HashSet 更省内存(在范围不大时尤其明显)。

成员检查(黑白名单)

你有 0~1,000,000 的 ID,频繁判断“某 ID 是否在白名单里”。

  • 用位图预处理白名单

  • 每次查询只查一位

  • 时间复杂度 O(1),非常快

位图排序

如果数据是“范围已知的非负整数”,可以:

  1. 扫描一遍,把出现过的数字位置设 1

  2. 从小到大扫描位图,遇到 1 就输出数字

输出天然有序。
这在“海量整数、内存紧张”时很经典。

适合VS不适合的场景

适合:

  • 数据是整数

  • 值域可控(比如 0~几千万)

  • 只关心“是否存在/是否命中”这类布尔状态

不适合:

  • 值域极大且非常稀疏(例如最大值 10^12,只出现几百个)

  • 需要存的不只是存在性(比如还要存次数、对象内容)

布隆过滤器核心数据结构就是使用的位图

海量QQ号去重

以腾讯的面试题为切入点来讲解位图的使用:

40亿个QQ号如何去重,只能使用1GB内存。

解析:一个整数占用4个字节,40亿个整数需要大概14.9G内存。此时可以使用位图来解决

位图一个字节能存8bit,4个字节能存32个数也就是说内存占用小32倍,完全符合条件

示例代码

我使用了RoaringBitmap来实现