用位图解决大量整数只出现一个,不超过两次,文件交集问题

先略提下位图:位图是建立在哈希表的基础上进行的优化,哈希表可能用1个字节或者4个字节来表示key,如果数据太多,哈希表就会占用大量空间。为了解决这个问题,聪明的人类发明了了位图。找到key的用一个bit来存储一个数的状态,0表示不存在,1表示存在。这样查找key是只需判断对应bit是1还是0就行,解决了哈希表占用空间的问题。

例题:位图应用

给定100亿个整数,设计算法找到只出现一次的整数

使用位图,就是用1个bit位表示一个状态(或者表示次数),可以缓解大量数据相关问题。
100亿个整数,如果是int型的话,在32位机器下int型表示范围为-2^31~2^31-1,共2^32个数字。而100亿个数字的话,远远超出int表示范围,这就意味着有大量数据重复了,而且有些数据可能出现不止一次。但既然这个题目让我们找到只出现一次的整数,那么我们用2个bit来表示一个整数出现次数就完全O什么K了。从左向右存储表示,00表示出现0次,10表示出现1次,11表示出现不止一次。
2个bit表示一个数字,如果要存储2^32个数字的状态就需要2 ^ 32 / 8 * 2字节的空间。一个字节就可以存储4个整数的状态了。我们用数组的方式存取数据。key表示查找表示所要查找的整数,因为2个bit表示一个数字,所以key/16才是存储key的元素下标,key%16和key%16+1就是key的两个状态位。用1左移对应长度,然后比较,就可判断key是否出现一次。
代码:
BitMap.h

#pragma once

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

typedef struct BitMap
{
    size_t* _bits;
    size_t _range;
}BitMap;

void BitMapInit(BitMap* bm, size_t range);
void BitMapSet(BitMap* bm, size_t x);
void BitMapReset(BitMap* bm, size_t x);
// x存在返回0,不存在返回-1 
int BitMapTest(BitMap* bm, size_t x);
void BitMapOnceTime(BitMap*bm, size_t x);//数字出现一次

BitMpa.c

#include"BitMap.h"

void BitMapInit(BitMap* bm, size_t range)//初始化
{
    bm->_bits = (size_t*)malloc(range / 4 + 1);
    assert(bm);
    memset(bm->_bits, 0, range / 4 + 1);
}

void BitMapSet(BitMap* bm, size_t x)//置1,两个bit表示状态,00、10、11表示0次,1次,2次及以上
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    if (BitMapTest(bm,x) == -1)//出现0次
    {
        bm->_bits[_index] |= (1 << _position);
    }
    else
    {
        if  (((bm->_bits[_index]) & (1 << (_position + 1))) == 0)//出现1次
            bm->_bits[_index] |= (1 << (_position + 1));
        else//出两次及以上,不变
            return;
    }
}

void BitMapReset(BitMap* bm, size_t x)//当前位置清零
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    bm->_bits[_index] &=  ~(1 << _position);
    bm->_bits[_index] &= ~(1 << (_position + 1));
}

// x存在返回0,不存在返回-1 
int BitMapTest(BitMap* bm, size_t x)
{

    int _index = x / 16;
    int _position = x % 16 * 2;

    if ((((bm->_bits[_index]) & (1 << _position)) != 0)
        || (((bm->_bits[_index]) & (1 << (_position + 1))) != 0))
        return 0;
    else
        return -1;
}

void BitMapOnceTime(BitMap*bm, size_t x)//数字出现一次
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    if ((((bm->_bits[_index]) & (1 << _position)) != 0)
        && (((bm->_bits[_index]) & (1 << (_position + 1))) == 0))
        printf("%u appear once.\n",x);
    else
        printf("%u isn't once.\n",x);
}

Test.c

#include"BitMap.h"

void TestAppearOnce()
{
    BitMap bt;
    BitMapInit(&bt, -1);//range是size_t型,-1存进去就是全1,表示整型最大数
    BitMapSet(&bt, 99);//只能插入非负数
    BitMapSet(&bt, 99);
    BitMapSet(&bt, 16);
    BitMapSet(&bt, 16);
    BitMapSet(&bt, 16);
    BitMapSet(&bt, 88);
    BitMapSet(&bt, 1024);
    BitMapSet(&bt, 4200000000);

    BitMapOnceTime(&bt, 99);
    BitMapOnceTime(&bt, 16);
    BitMapOnceTime(&bt, 88);
    BitMapOnceTime(&bt, 1024);
    BitMapOnceTime(&bt, 4200000000);
}


int main()
{
    TestAppearOnce();

    return 0;
}

1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数

100亿个int类型的数据,则数据范围在-2^31~2^21-1,2个bit表示一个数据出现的次数,只需2^32 * 2个bit就可。而2^32*2bit就是1G,因此内存使用满足题目条件。用从左向右存储,00、10、01、11分别表示0次,1次,2次,3次。类似100亿个整数查找只出现一次的整数,即可找到满足题目要求的整数。
代码
BitMapAppearNoMoreTwice.h

//1个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次的所有整数
#pragma once

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

typedef struct BitMap
{
    size_t* _bits;
    size_t _range;
}BitMap;

void BitMapInit(BitMap* bm, size_t range);
void BitMapSet(BitMap* bm, size_t x);
void BitMapReset(BitMap* bm, size_t x);
void BitMapAppearNoMoreTwiceTime(BitMap*bm, size_t x);//数字出现一次
void Destory(BitMap* bt);//销毁

BitMapAppearNoMoreTwice.c

#include"BitMapAppearNoMoreTwice.h"

void BitMapInit(BitMap* bm, size_t range)//初始化
{
    bm->_bits = (size_t*)malloc(range / 4 + 1);
    assert(bm);
    memset(bm->_bits, 0, range / 4 + 1);
}

//2个bit位表示一块,标识一个数。0-2147483647块标识正数,2147483647-4294967295块标识负数
void BitMapSet(BitMap* bm, size_t x)//置1,两个bit表示状态,00、01、10、11表示0次,1次,2次、3次及以上
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    if ((((bm->_bits[_index]) & (1 << _position)) == 0)
        && (((bm->_bits[_index]) & (1 << (_position + 1))) == 0))//出现0次
    {
        bm->_bits[_index] |= (1 << _position);
    }

    else if ((((bm->_bits[_index]) & (1 << _position)) != 0)
        && (((bm->_bits[_index]) & (1 << (_position + 1))) == 0))//出现1次
    {
        bm->_bits[_index] |= (1 << (_position + 1));//高位置1
        bm->_bits[_index] &= ~(1 << _position);//低位置0
    }

    else if ((((bm->_bits[_index]) & (1 << _position)) == 0)
        && (((bm->_bits[_index]) & (1 << (_position + 1))) != 0))//出现两次
    {
        bm->_bits[_index] |= (1 << _position);//低位置1
    }

    else
        return;

}

void BitMapReset(BitMap* bm, size_t x)//当前位置清零
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    bm->_bits[_index] &= ~(1 << _position);
    bm->_bits[_index] &= ~(1 << (_position + 1));
}


void BitMapAppearNoMoreTwiceTime(BitMap*bm, size_t x)//数字出现不超过两次
{
    assert(bm);

    int _index = x / 16;
    int _position = x % 16 * 2;

    if ((((bm->_bits[_index]) & (1 << _position)) != 0)
        && (((bm->_bits[_index]) & (1 << (_position + 1))) != 0))
    {
        printf("%d appear more than twice.\n", x);
    }
    else
    {
        printf("%d appear no more twice.\n", x );
    }

}

void Destory(BitMap* bt)//销毁
{
    assert(bt);

    free(bt->_bits);
    bt->_bits = NULL;
    bt->_range = 0;
}

Test.c

#include"BitMapAppearNoMoreTwice.h"

void TestAppearOnce()//只能插入-2147483648到2147483647之间的数,因为这是int型范围
{
    BitMap bt;
    BitMapInit(&bt, -1);//range是size_t型,-1存进去就是全1,表示整型最大数
    BitMapSet(&bt, 99);
    BitMapSet(&bt, 99);
    BitMapSet(&bt, -16);
    BitMapSet(&bt, -16);
    BitMapSet(&bt, 88);
    BitMapSet(&bt, 88);
    BitMapSet(&bt, 88);
    BitMapSet(&bt, 1024);
    BitMapSet(&bt, 1024);
    BitMapSet(&bt, 1024);
    BitMapSet(&bt, 1024);
    BitMapSet(&bt, -2100000000);
    BitMapSet(&bt, -2100000000);

    BitMapAppearNoMoreTwiceTime(&bt, 99);
    BitMapAppearNoMoreTwiceTime(&bt, -16);
    BitMapAppearNoMoreTwiceTime(&bt, 88);
    BitMapAppearNoMoreTwiceTime(&bt, 1024);
    BitMapAppearNoMoreTwiceTime(&bt, -2100000000);

    Destory(&bt);
}


int main()
{
    TestAppearOnce();

    return 0;
}

给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集

两个文件,一个bit位就可标识整数key是否存在。2^32bit就可以表示一个文件,2个文件共需1G,满足题意。然后判断对应位置状态位是否都为1即可。
代码
BitMap.h

#pragma once

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

typedef struct BitMap
{
    size_t* _bits;
    size_t _range;
}BitMap;

void BitMapInit(BitMap* bm, size_t range);
void BitMapSet(BitMap* bm, size_t x);
void BitMapReset(BitMap* bm, size_t x);
int BitMapTest(BitMap* bm, size_t x);// x存在返回0,不存在返回-1 
void JudgeTwoBitMapSame(BitMap *bt1, BitMap *bt2);//判断两个位图相同地方
void Destory(BitMap* bt);//销毁

BitMap.c

#include"BitMap.h"

void BitMapInit(BitMap* bm, size_t range)
{
    bm->_bits = (size_t*)malloc(range / 8 + 1);
    bm->_range = range;
    assert(bm);
    memset(bm->_bits, 0, bm->_range / 8 + 1);
}

void BitMapSet(BitMap* bm, size_t x)
{
    assert(bm);

    int _index = x / 32;
    int _position = x % 32;

    bm->_bits[_index] |= (1 << _position);
}

void BitMapReset(BitMap* bm, size_t x)
{
    assert(bm);

    int _index = x / 32;
    int _position = x % 32;

    bm->_bits[_index] &= ~(1 << _position);

}

// x存在返回0,不存在返回-1 
int BitMapTest(BitMap* bm, size_t x)
{
    int _index = x / 32;
    int _position = x % 32;

    if (((bm->_bits[_index]) & (1 << (_position))) != 0)
        return 0;
    else
        return -1;
}


void JudgeTwoBitMapSame(BitMap *bt1, BitMap *bt2)//判断两个位图相同地方
{
    assert(bt1);
    assert(bt2);
    size_t i = 0;
    int _index;
    int _position;

    for (i = 0; i < bt1->_range; i++)//此处i<bit1的范围,而bt1->_range=2^32,循环会进行较长时间\
        且不能判断2^32-1位置的数,若i=2^32-1则会死循环
    {
        _index = i / 32;
        _position = i % 32;
        if (((bt1->_bits[_index] & (1<<_position)) != 0) 
            && ((bt2->_bits[_index] & (1 << _position)) != 0))
            printf("%u ", i);
    }

}

void Destory(BitMap* bt)//销毁
{
    assert(bt);

    free(bt->_bits);
    bt->_bits = NULL;
    bt->_range = 0;
}

Test.c

#include"BitMap.h"


void TestTwoBitMapSame()
{
    BitMap bt1;
    BitMap bt2;
    BitMapInit(&bt1, -1);//range是size_t型,-1存进去就是全1,表示整型最大数
    BitMapInit(&bt2, -1);

    BitMapSet(&bt1, 99);//只能插入非负数
    BitMapSet(&bt1, 666);
    BitMapSet(&bt1, 16);
    BitMapSet(&bt1, 2333);
    BitMapSet(&bt1, 11);
    BitMapSet(&bt1, 88);
    BitMapSet(&bt1, 1996);
    BitMapSet(&bt1, 1024);
    BitMapSet(&bt1, 1024);
    BitMapSet(&bt1, 4294967294);//4294967294该数是最大存储的数字,2^32-1-1,\
                                因为位图从0开始存储,空间是2^32-1,



    BitMapSet(&bt2, 100);
    BitMapSet(&bt2, 6954);
    BitMapSet(&bt2, 759);
    BitMapSet(&bt2, 2048);
    BitMapSet(&bt2, 2333);
    BitMapSet(&bt2, 16);
    BitMapSet(&bt2, 1024);
    BitMapSet(&bt2, 1996);
    BitMapSet(&bt2, 4294967294);

    JudgeTwoBitMapSame(&bt1, &bt2);
    printf("\n");

    BitMapReset(&bt2, 1996);
    JudgeTwoBitMapSame(&bt1, &bt2);
    printf("\n");

    Destory(&bt1);
    Destory(&bt2);

}

int main()
{

    TestTwoBitMapSame();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值