现在的位置: 首页 > 综合 > 正文

数据结构之位图(11)

2017年12月14日 ⁄ 综合 ⁄ 共 2397字 ⁄ 字号 评论关闭

1、原理

位图是通过将数组下标与应用中的一些值关联映射,数组中该下标所指定的位置上的元素可以用来标识应用中值的情况(是否存在或者数目 或者计数等),位图数组中每个元素在内存中占用1位,所以可以节省存储空间。位图是一种非常简洁快速的数据结构,它能同时使存储空间和速度最优化。如可用一个10位长的字符串来表示一个所有元素都小于10的简单的非负整数集合,例如,可以用如下字符串表示集合{1,2,4,5,8}
,对应位置数字存在标记为1,否则标记为0。

2、位图实现

2.1 C语言位图实现

所申请的int数组如下所示:


字节位置=数据/32;(采用位运算即右移5位)

位位置=数据%32;(采用位运算即跟0X1F进行与操作)。

#include <stdio.h>
#define MAX 10000000
#define SHIFT 5           
#define MASK 0x1F
#define DIGITS 32


int a[1+MAX/DIGITS];

void set(int n)                                //将逻辑位置为n的二进制位置为1 
{
    a[n>>SHIFT] |=(1<<(n&MASK));               //n>>SHIFT右移5位相当于除以32求算字节位置,n&MASK相当于对32取余即求位位置,
}                                                

void clear(int n)
{
    a[n>>SHIFT] &=(~(1<<(n&MASK)));           //将逻辑位置为n的二进制位置为0
}

int test(int n)
{
    return a[n>>SHIFT] & (1<<(n&MASK));        //测试逻辑位置为n的二进制位是否为1 
}

int main(int argc, char *argv[])
{
    int i,n;
    for(i=1;i<=MAX;i++)
    {
        clear(i);
    }    
    while(scanf("%d",&n)!=EOF)
    {
        set(n);
    }
    for(i=1;i<=MAX;i++)
    {
        if(test(i))
            printf("%d ",i);
    }
    return 0;
}

2.2 JAVA位图实现-BitMap类

BitMap类大小可动态改变, 取值为true或false的位集合。用于表示一组布尔标志。此类实现了一个按需增长的位向量。
BitMap的每个组件都有一个 boolean 值。默认情况下,BitMap中所有位的初始值都是 false。 BitMap是线程不安全的类。

BitSet bitSet=new BitSet(10000000);
bitSet.set(999, true);
System.out.println(bitSet.get(999));
System.out.println(bitSet.size());

3、应用

3.1 操作系统中的应用

在操作系统的存储管理和文件系统中可用位图分别记录内存和磁盘的空闲区域。
1、记录内存中空闲区域的数据结构(空闲内存管理)
1)链表
2)位图:在位图中,空闲用1表示,已分配用0表示。

使用位图方法时,内存可能被划分成小道几个字或大到几千字节的分配单元。每个分配单元对应于位图中的一位,0表示空闲,1表示占用。内存与位图的对应关系见图。


这种方法的问题是,在决定把一个占k个分配单元的进程调入内存时,存储管理器必须搜索位图,在位图中找出有k个连续0的串。位图的缺点是在位图中查找指定长度的连续0串是耗时的操作。

2、记录磁盘中空闲块的数据结构(空闲磁盘管理):

1)磁盘块链表

2)位图:n个块的磁盘需要n位位图。在位图中,空闲块用1表示,已分配块用0表示。

3.2 位图法搜索和排序:找重复数字或缺失数字,排序

题目1:在2.5亿整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数?如果对2.5亿个数字排序,怎么办?
类似题目:给你一堆电话号码列表,数量大概在千万级,要求从中找出所有重复的电话号码,需要时间复杂度尽可能小。
题目2:腾讯面试题:给40亿个不重复的unsinged int的整数,没排过序,然后再给出一个数,如何快速判断这个数是否在那40亿个数当中?
题目3:海量数据排序问题:文件包含1千万条电话号码记录(10**7次方),每条记录都是7位整数,没有重复的整数。要求对文件进行排序,注意大约只有1MB的内存空间可用,有充足的磁盘存储空间可用。请设计一个高效的算法

思路1:如果有足够的内存,
1)按照集合中最大元素(或者集合中数据数目)max创建一个长度为max+1的数组。数组下标代表对应数字,数组各个元素值标记初始值为0。
2)赋初始值:根据数字是否在集合中在数组对应值中做标记,如在集合中标记为1
3)扫描数组:对于找数字是否在集中的题目,可以在数组对应位置查看标记是否为1,为1表示数字在集合汇总,为0表示数字不在集合中。

对于找重复数字的题目,可以在第2步赋值的时候获得如下信息:如果集合中数字在对应位置赋值时,发现数组值已为1,则这个数字是重复的,输出该数字。
时间复杂度O(2N)。排序:如果排序的话,按照位图数组输出数组值为1的数组的下标就可以了。
    但是现在的问题是当数据量很大时,内存无法存放如此大的数据量。如用整型数组存放10000000000个数字,JAVA中整型是4个字节,所以共占用内存40GB
int[] a = new int[10000000000];
boolean[] a = new boolean[10000000000];
思考:如何解决?因为我们在数组的值中只需存取值0和1,所以最简单的0和1就够用了,能表示0和1的最小存储单位是什么呢?是内存中的一位。
所以可以用位图数组大大减小存储空间。
思路2:可以用外部排序(合并排序),然后查找的方法

其他应用:设计搜索剪枝时,需要保存已经搜索过的历史信息,有些情况下,可以使用位图减小历史信息数据所在空间。


参考文献:

[1]现代操作系统

[2]编程珠玑

[3] http://www.cnblogs.com/biyeymyhjob/archive/2012/08/14/2636933.html

抱歉!评论已关闭.