工学1号馆

home

« | 返回首页 | »

位图算法

By Wu Yudong on June 01, 2015

原创文章,转载请注明: 转载自工学1号馆

位图法定义

位图法就是bitmap的缩写,所谓bitmap,是用每一位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。

例如,要判断一千万个人的状态,每个人只有两种状态:男人,女人,可以用0,1表示。那么就可以开一个int数组,一个int有32个位,就可以表示32个人。操作的时候可以使用位操作。

数据结构

unsigned int bit[N];
在这个数组里面,可以存储 N * sizeof(int) * 8个数据,但是最大的数只能是N * sizeof(int)  * 8 - 1。假如,我们要存储的数据范围为0-15,则我们只需要使得N=1,这样就可以把数据存进去。如下图:
数据为【5,1,7,15,0,4,6,10】,则存入这个结构中的情况为

位图法应用

一、给40亿个不重复的unsigned int的整数,没排过序的,然后再给一个数,如何快速判断这个数是否在那40亿个数当中申请512M的内存,一个bit位代表一个unsigned int值,读入40亿个数,设置相应的bit位,读入要查询的数,查看相应bit位是否为1,为1表示存在,为0表示不存在

二、使用位图法判断整形数组是否存在重复

判断集合中存在重复是常见编程任务之一,当集合中数据量比较大时我们通常希望少进行几次扫描,这时双重循环法就不可取了。位图法比较适合于这种情况,它的做法是按照集合中最大元素max创建一个长度为max+1的新数组,然后再次扫描原数组,遇到几就给新数组的第几位置上1,如遇到 5就给新数组的第六个元素置1,这样下次再遇到5想置位时发现新数组的第六个元素已经是1了,这说明这次的数据肯定和以前的数据存在着重复。这种给新数组初始化时置零其后置一的做法类似于位图的处理方法故称位图法。它的运算次数最坏的情况为2N。如果已知数组的最大值即能事先给新数组定长的话效率还能提高一倍。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>

bool hasDuplicatedItem(int *a, int len)
{
    int length, max, i; 
    length = len;
    max = a[0];
    for(i = 1; i < length; i++){
        if(a[i] > max)
            max = a[i];
    }
    int *arr;
    arr = (int*)malloc(sizeof(int) * (max + 1));
    for(i = 0; i < length; i++){
        if(arr[a[i]])
            return true;
        else
            arr[a[i]] = 1;
    }
    return false;
}

int main()
{
    int length;
    int test[] = {0,1,2,3,45,12,13};
    length = (sizeof(test) / sizeof(test[0]));
    if(hasDuplicatedItem(test, length))
        printf("hasDuplicatedItem!n");
    else
        printf("hasNoDuplicatedItem!n");
    return 0;
}

三、使用位图法进行整形数组排序

首先遍历数组,得到数组的最大最小值,然后根据这个最大最小值来缩小bitmap的范围。这里需要注意对于int的负数,都要转化为unsigned int来处理,而且取位的时候,数字要减去最小值。

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stdbool.h>

void bitmapSort(int *a, int len)
{
    int length, max, min, i, index; 
    length = len;
    min = max = a[0];
    //找出数组最大值
    for(i = 1; i < length; i++){
        if(a[i] > max){
            max = a[i];
        }
        if(min > a[i]) {
            min = a[i];
        }
    }
    //得到位图数组
    int *arr;
    arr = (int*)malloc(sizeof(int) * (max - min + 1));
    for(i = 0; i < length; i++){
        index = a[i] - min;
        arr[index]++;
    }
    //重整a中的元素
    int arr_length;
    arr_length = max - min + 1;
    index = 0;
    for(i = 0; i < arr_length; i++){
        while(arr[i] > 0){
            a[index] = i + min;
            index++;
            arr[i]--;
        }
    }
}

void print(int *a, int n)
{
    int i;
    for(i = 0; i < n; i++) {
        printf("%d ", a[i]);
    }
    printf("n");
}

int main()
{
    int length;
    int test[] = {50,1,26,3,45,12,13};
    length = sizeof(test) / sizeof(test[0]);
    print(test, length);
    bitmapSort(test, length);
    print(test, length);
    return 0;
}

四、位图法存数据

输入:一个最多包含n个正整数的文件,每个数都小于n,其中n=10,000,000 输入文件中没有重复的整数,没有其他数据与该整数相关联。

输出: 按升序排列这些数。

约束:有 1MB多(不超过2MB) 的内存空间可用,有充足的硬盘空间。

#include<stdio.h>
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
#define N 10000000
int a[1 + N/BITSPERWORD];

/* a[i>>SHIFT]是第i位应该在第几个int上 */
/* (1<<(i & MASK))是第i位在该int上的第几个bit */

void set(int i)
{
    a[i>>SHIFT] |= (1<<(i & MASK));
}

void clr(int i)
{
    a[i>>SHIFT] &= ~(1<<(i & MASK));
}

int test(int i)
{
    return a[i>>SHIFT] & (1<<(i & MASK));
}

int main()
{
    int i;
    for(i = 0; i < N; i++)
        clr(i);
    while(scanf("%d", &i) != EOF)
        set(i);
    for(i = 0; i < N; i++)
        if(test(i))
            printf("%dn", i);
    return 0;
}

如果文章对您有帮助,欢迎点击下方按钮打赏作者

Comments

No comments yet.
To verify that you are human, please fill in "七"(required)