[珠玑之椟]位向量/位图的定义和应用

简介:

位向量/位图是一个很有用的数据结构,在充分利用小空间存储大量数据方面非常具有优势,Linux内核中很多地方都是用了位图。同时,它不但基础,而且用到了很多编程语言的知识,以及对细节的把握,常常作为面试题出现。这里将要介绍它的实现、操作、应用。

  与位图(bitmap)比,我更倾向于用位向量(bit vector),前者比较容易与图形学里的名词混淆,其实提到位图,多指的是“是使用像素阵列来表示的图像”(维基百科),为了避免这一点,下文中使用位向量。先来看看产生位向量的需求:

  一般地,对于多个对象和一个性质,这些对象可能满足(true)也可能不满足(false)这条性质。那么,为了表示所有对象对这个性质的满足情况,最容易想到的方式是分配一个int型变量(这里不讨论布尔型,C++没有对布尔型占用空间有明确规定;本文主要讨论C)作为标志,用1表示满足条件,用0表示不满足条件。同时为了方便查询,把这些对象的标志整合成一个数组。显然,使用int来表示是0还是1有些太浪费空间了,即使把int改为char,浪费的情况也只是减轻了一部分,仍有很大的空间被浪费。考虑到计算机中最小的数据单位是非0即1的二进制位,对于一个对象,使用一个二进制位就足够了。很多语言都具有位运算,利用位运算是可以完成本段开始处提出的要求的。当然,因为不能用一个变量名直接表示一个位,那么可以将多个位组合成一个基本数据类型,通过对这个基本数据类型进行操作,达到使用位的方法。同时,为了方便,延续使用int数组的做法,把这些由位组合成的基本数据类型也组成数组。

  经过这样分析,位向量的实现方法大体是:多个位组成一个基本数据类型,基本数据类型组合成数组。根据这个思路就可以写出位向量的表示了。在阅读下面代码前,建议读者尝试自己独立完成,这是一些提示:简单起见,使用int作为位组成的基本数据类型,且int使用32位表示;int数组中元素的个数如何计算?

位向量的表示

  写完了表示,就需要为这个数据结构增加对应的操作了。对于每个位的操作,有三种:设置为0、设置为1、读取当前值。根据上文位向量的表示,实现这三种操作。同样建议读者先尝试独立完成。以下代码参考自《编程珠玑》习题1.2。

位向量操作

  使用位向量前不要忘记对所有位进行初始化:

for(i=0;i<N;i++)
    clr(i);

  当然,你也可以用这个或许更快的初始化方式:

int temp = N/BITPERWORD+1;
for (i=0; i< temp;i++) 
    a[i] = 0;

补充讨论:

Q:为什么要用看上去并不那么直接的位运算而不是i/32和i%32?

A:为了速度而不是易读性。你所写下的最初版本如果使用的是/和%,当然没有错;当你着手于提高效率的时候,把它们改成移位运算就势在必行了。虽然编译器可能会对/和%在除数为2的幂时进行优化,写成移位运算,你当然可以自己来完成这件事来保证确实优化了。

 

Q:是否可以将存放位向量的数组作为参数?

A:当然可以,上文代码只是为了叙述方便。

 

Q:是否可以把这三个函数写成宏?

A:其实我曾经被面试过一道题,就是要求写一个宏,完成上面clr(i)的任务。写成宏当然没问题,注意加好括号就行,具体的用法让调用的人去担心吧(笑)。

 

Q:位向量和位字段/位域有什么区别和联系?可以用位字段来实现位向量吗?

A:C语言允许将结构的整数成员存入比编译器通常允许的更小的空间里。然而它有三个风险:不同计算机中对齐限制不同;位字段宽度限制不同;将位包装成字的字节顺序不同。在位向量里,虽然也有依赖于具体计算机特性的限制(需要预先知道int的位数),但是位的包装是由程序员来控制的,也不必考虑对其限制。移植性略好于将成员声明为1位整数位字段的结构。

 

应用:

1.Linux中分配唯一pid的算法、内存管理的伙伴分配系统等,详细可以google,关键词:linux+位图。

2.一个最多包含n个正整数的文件,每个数都小于n,其中n=107,并且没有重复。最多有1MB内存可用。要求用最快方式将它们排序并按升序输出。(《编程珠玑》第一章正文)方法是一次读入文件,把出现过的数字对应位置1;读取完毕后从低位到高位输出位向量为1的位所代表的数。

3.如果有用时间换空间的必要,可以将寻找1至某个数之间所有质数的埃氏筛法用位向量实现。原始版本的代码见于《编程珠玑(续)》习题1.2,算法简介:一个n比特的数组,对应1至n-1,初值均为真。从2开始,每发现一个质数,就把这个数组中所有这个数的倍数设为假。下一个质数就是数组中下一个为真的比特。循环至所有数都被遍历,即可得到该范围内所有的质数。

 

引申和扩展:

1.(习题1.6)每个整数最多出现10次而不是1次;如果内存限制不变,又应该怎么做?提示:多趟排序

(更一般的扩展:用几个位表示一个对象的多个性质,也即一个对象不仅仅只占用一位而是多位,但每个对象占用的位数是相同的。重写位图。)

2.(习题1.9)对于稀疏的位向量,初始化很浪费时间。一般地,对于一个向量(不仅限于位向量),怎样利用辅助数据结构,使得第一次访问位向量的某一位时才将其初始化为0?

解法:

对于向量int data[N],分配辅助数据结构int from[N]和to[N],以及一个整数top,初始化top=0。

插入data[i]时,from[i]填入top,to[top] = i,top++;

这样维持了一个性质:top代表下一个被插入的值的次序;from[i]代表了data[i]是第几个被插入的;to[]中小于top的元素to[j]保存了第j个被插入对应在data[]的下标。

若data[i]未初始化,则from[i]也未初始化,此时即使from[i]中未初始化的垃圾值<top,to[from[i]]!= i,仍能判断出data[i]未初始化。

 

 

“珠玑之椟”系列简介与索引

 





本文转自五岳博客园博客,原文链接:www.cnblogs.com/wuyuegb2312/p/3136831.html,如需转载请自行联系原作者

目录
相关文章
|
计算机视觉
数字图像处理实验(七)| 形态学图像处理{生成结构元素strel、腐蚀运算imerode、膨胀运算imdilate、开运算imopen、闭运算imclose}(附代码和实验截图、汉字视力表项目、总结)
数字图像处理实验(七)| 形态学图像处理{生成结构元素strel、腐蚀运算imerode、膨胀运算imdilate、开运算imopen、闭运算imclose}(附代码和实验截图、汉字视力表项目、总结)
750 0
数字图像处理实验(七)| 形态学图像处理{生成结构元素strel、腐蚀运算imerode、膨胀运算imdilate、开运算imopen、闭运算imclose}(附代码和实验截图、汉字视力表项目、总结)
|
6月前
|
计算机视觉
图像处理之给定任意四点不规则放缩
图像处理之给定任意四点不规则放缩
31 3
|
7月前
|
算法 计算机视觉
OpenCV(四十四):亚像素级别角点位置优化
OpenCV(四十四):亚像素级别角点位置优化
207 0
C#编程练习(02):大地坐标系(LBH)向空间直角坐标系(XYZ)的转换及其逆转换
C#编程练习(02):大地坐标系(LBH)向空间直角坐标系(XYZ)的转换及其逆转换
C#编程练习(02):大地坐标系(LBH)向空间直角坐标系(XYZ)的转换及其逆转换
|
存储 API C#
C#编程学习12:使用ArcEngine+C#进行栅格数据读取和像素值修改思路剖析
C#编程学习12:使用ArcEngine+C#进行栅格数据读取和像素值修改思路剖析
|
Perl
【刷题记录】6. Z 字形变换
【刷题记录】6. Z 字形变换
130 0
【刷题记录】6. Z 字形变换
|
Android开发 Windows iOS开发
|
JavaScript Android开发 iOS开发
|
存储 编解码 Android开发