一种很有意思的数据结构:Bitmap
昨晚遇到了一种很有意思的数据结构,Bitmap
。
Bitmap
,准确来说是基于位的映射。其中每个元素均为布尔型(0 or 1),初始均为 false(0)。位图可以动态地表示由一组无符号整数构成的集合。 每个bit对应一个无符号数。如位图第10个比特为true(1),表示无符号整数9。
之所以用位图来表示整数,是为了 节省 内存。假如要处理50亿个四字节无符号整数,那么需要 5,000,000,000 * 4bytes = 20,000,000,000bytes = (20,000,000,000 / 1024 / 1024 / 1024)GB = 18.63GB
内存,显然普通的计算机无法处理。如果使用位图的话,每个位映射为一个数,则只需要 1bit * 5,000,000,000 = (5,000,000,000 / 8)bytes = (625,000,000 / 1024 / 1024)MB = 596MB
,需要的内存降到了 1/32
。
如何由一个整数k定位到指定位置?
思路是先定位到第k比特所在的字节位置,然后在计算出它所在这个字节的第几位。
因为一个字节(char)占8位,所以可以由 k >> 3
算出第k比特所在字节位置,这里设找到的这个字节位 b
。
然后计算它所在8位字节 b
的第几位,用 k % 8
即可得到,写成位运算 k & 0x07
,获得低三位所等价的数字,这里设为 x
。
定位到了指定位,如何修改它?
已经找到了第k比特在字节 b
的 第 x
位。如果要将k添加进位图,就需要将字节 b
的 第 x
位设置为1,只需用一个第 x
位为1的字节与字节 b
做或运算即可。构造这样的字节:0b1000_0000 >> x
(就是:0x80 >> (k & 0x07)
),就可以将字节第 x
为设为1。然后通过位运算 |
,M[k >> 3] |= (0x80 >> (k & 0x07))
。
完整的 Bitmap C++代码:
- //代码来自邓俊辉数据结构习题集
- class Bitmap {
- private:
- char *M; //比特图所存放的空间为M[]
- int N; //容量为 N*sizeof(char)*8,就是N*8,一个字节8位
- protected:
- void init(int n) {
- M = new char[N = (n + 7) / 8];
- memset(M, 0, N); //将指针M后的N个字节用0代替,初始化位图,位全设置为0
- }
- public:
- Bitmap(int n = 8) { //按指定规模创建比特图
- init(n);
- }
- Bitmap(char* file, int n = 8) { //从文件中读取比特图
- init(n);
- FILE* fp = fopen(file, "r");
- fread(M, sizeof(char), N, fp);
- fclose(fp);
- }
- ~Bitmap() { //释放比特图空间
- delete[] M;
- M = nullptr;
- }
- void set(int k) { //设置第k个比特为1
- expand(k);
- M[k >> 3] |= (0x80 >> (k & 0x07));
- /*
- * 因为每个字节包含8个比特,所以通过位运算 k>>3 可以确定对应的比特所属第几个字节。
- * 通过逻辑位与运算 k & 0x07[0b0111] 可以确定比特位所属字节的第几个位。获得k的低3比特,即比特在字节中的位置,设为x。
- * 通过移位操作 0x80[128:0b1000_0000] >> (k & 0x07) 可以得到该比特位所在字节中对应的数值的掩码。
- * 将(128:0b1000_0000)中最高位的1右移x。即将x位设置为1。然后 |=,将M第k字节的第x为设为1。
- */
- }
- void clear(int k) { //第k个比特清零
- expand(k);
- M[k >> 3] &= ~0x80 >> (k & 0x07);
- /*
- * ~0x08 是 0b01111111,将最高位0右移到第x位,然后 &= 就是设M第k个字节的第x位为0.
- */
- }
- bool test(int k) { //判断是否存在k
- expand(k);
- return M[k >> 3] & (0x80 >> (k & 0x07));
- /*
- * 判断M第k个字节的第x位是否为1
- */
- }
- void dump(char* file) {
- FILE* fp = fopen(file, "w");
- fwrite(M, sizeof(char), N, fp);
- fclose(fp);
- }
- char* bits2string(int n) { //将前n位转换为字符串
- expand(n - 1); //此时可能被访问的最高位为Bitmap[n-1]
- char* s = new char[n + 1];
- s[n] = '\0';
- for (int i = 0; i < n; i++)
- s[i] = test(i) ? '1' : '0';
- return s;
- }
- void expand(int k) { //若被访问的Bitmap[k]已出,则需扩容
- if (k < 8 * N) //无需扩容
- return;
- int oldN = N;
- char* oldM = M;
- init(2 * k); //加倍扩容
- memcpy_s(M, N, oldM, oldN); //原数据转移至新空间
- delete[] oldM;
- }
- };
应用:
可进行数据的快速查找,判重,删除。直接将整数k做位运算即可,复杂度为O(1)。
一种很有意思的数据结构:Bitmap的更多相关文章
- 在不开启事件循环的线程中使用QTimer(QThread::run函数自带事件循环,在构造函数里创建线程,是一种很有意思的线程用法) good
引入 QTimer是Qt自带的定时器类,QTimer运行时是依赖于事件循环的,简单来说,在一个不开启事件循环(未调用exec() )的线程中,QTimer是无法使用的.通过分析Qt源码可发现,调用QT ...
- 关于Java异常一段很有意思的代码
今天学习了Java的异常,讲到try-catch-finally时,老师演示了一段代码,觉得很有意思,很能反映出其执行的过程,让自己有点绕,特意记录一下. 只要代码执行到try代码内部, 不管有没有异 ...
- Gym - 101291C (很有意思的最短路)
题意: 给出一张地图和机器人还有出口的位置,地图上面有障碍.然后给出UDLR上下左右四种指令,遇到障碍物或者越界的指令会忽略,剩下的继续执行. 只要到达出口就算找到出口,然后给你一串指令,让你修改指令 ...
- 一行css代码调试中学到的javascript知识,很有意思
现在到处都是JavaScript,每天都能知道点新东西.一旦你入了门,你总能从这里或是那里领悟到很多知识.今天我想分享Addy Osmani的一行代码 ,这行代码对于你调试你的CSS是很有用的.为了可 ...
- We7——很有意思的一个开源CMS
目前做门户.做网站,基本上都需要用到一个系统,那就是CMS内容管理系统:现在开源产品有很多,笔者也是从事这个行业的,国内的各大CMS提供商基本上都试用过,今天向大家推荐一款很有意思的产品——We7CM ...
- 介绍一种很棒的wince 如何替换系统声音的方法
Topic:介绍一种很棒的wince 如何替换系统声音的方法(作者:Baiduluckyboy) //------------------------------------------------- ...
- 一道很有意思的java线程题
这几天看结城浩的<java多线程设计模式>,跟着做一些习题,有几道题目很有意思,记录下自己的体会. 首先是题目(在原书212页,书尾有解答): public class Main { pu ...
- [Python][pythonchallenge][TBC]古老的python在线挑战赛,很有意思 (C0-C4)
预计阅读时间:15分钟 背景:搜索资料时候偶然发现的,很有意思,每一关都覆盖了很多知识点 Python版本:3.0 Talking is cheap,show me the code 主页: http ...
- 一套很有意思的C语言测试题目
网络上逛博客,发现了一套很有意思的测试题目: https://kobes.ca/ 大家有兴趣可以做一下,考一些关于C语言使用的细节: 中文翻译参考: https://www.cnblogs.com/l ...
随机推荐
- 数据挖掘中ID3算法实现zz
id3 function D = ID3(train_features, train_targets, params, region) % Classify using Quinlan's ID3 a ...
- Alpha冲刺 - (6/10)
Part.1 开篇 队名:彳艮彳亍团队 组长博客:戳我进入 作业博客:班级博客本次作业的链接 Part.2 成员汇报 组员1(组长)柯奇豪 过去两天完成了哪些任务 基于ssm框架的前后端交互测试,结合 ...
- R12.1.3 patch9239090
参考文档:Oracle E-Business Suite Release 12.1.3 Readme [ID 1080973.1]1.调整参数_disable_fast_validate=TRUEpg ...
- EBS11i - 常用Profile
Profile Name 说明 设置建议 FND: Enable Cancel Query 当执行一个超长时间的查询时,会出现一个 Cancel窗口,允许用户取消.比如我们在SO 界面没有输入条件 ...
- C#基础入门 八
C#基础入门 八 泛型 C#中的泛型能够将类型作为参数来传递,即在创建类型时用一个特定的符号,如"T"来作为一个占位符,代替实际的类型,等待实例化时用一个实际的类型来代替. pub ...
- 使用Arduino Wire Library读取温湿度传感器AM2321
AM2321是采用I2C总线或单总线通讯的国产温湿度传感器.在AM2321手册中,当采用I2C通讯时,手册指定了多处需要主机等待的时间间隔,包括: (1)唤醒传感器时,从机不回复ACK,但主机主要等待 ...
- 我对于C#的想法
前言 首先,感谢各位的回答,还看到了好几个大神过来回答受宠若惊,有叫我坚持的,有叫我放弃,感谢. 一开始我学的是Java,但是没有实际的工作经验,因为从工作开始到现在已经两年的时间了我用的一直都是C# ...
- 自己从0开始学习Unity的笔记 III (C#随机数产生基础练习)
自己开始尝试弄一下随机数,照着方法,自己做了个英雄打怪兽的测试 int heroAttack; ; ; Random attack = new Random(); //初始化一个随机数的类 heroA ...
- [uwp]自定义Behavior之随意拖动
由于最近有需求,所以自定义了一个随意拖动元素的Behavior. 当然在使用这个自定义的Behavior时,有个小假设:拖动元素必须是Canvas容器的子元素. 实现原理比较简单低效: 监听被拖动元素 ...
- Android 屏幕适应
基础知识: 屏幕密度: Density-independent pixel (dp):密度无关像素单位(一个相对的值).1dp 的大小相当于一个 160 dpi 屏幕上一个像素的大小. 计算方法:px ...