菜鸟笔记
提升您的技术认知

面试题

实现一个LRU Cache 算法LRU Cache C++三种解法java实现LRU算法及编码实现LRU策略缓存LRU算法常见缓存算法和LRU的c++实现设计循环双端队列(deque)LRU 缓存结构 (c++ 哈希双链表实现)LRU缓存机制删除单链表中的指定节点Linux 内核经典面试题拼多多社招面经:Redis是重点,讲一讲redis的内存模型线程、进程、协程的区别C++经典面试题面试官:我们只想要这样的C++工程师Linux C/C++ 学习路线链表操作汇总C++11的智能指针面试题浏览器中输入url后发生的事情常用的限流算法HTTP协议和HTTPS协议面试题网络编程面试题目总结c++后台面试题目如何实现LRU算法?如何寻找无序数组中的第K大元素?布隆过滤器 - 如何在100个亿URL中快速判断某URL是否存在?如何实现大整数相加?C++面试题及基本知识点总结C++给定出栈序列判定是否合法消息队列面试题要点redis缓存击穿,失效以及热点key解决方案网页在浏览器上的渲染过程几种限流算法lru算法例题C/C++常见面试知识点总结附面试真题----20210529更新引入MQ消息队列的作用及其优缺点MySQL面试篇社招三年后端面试题60道测开面试题,背完直接涨工资二叉树的层序遍历(两种方法实现)Bitmap 海量数据处理字符串倒序输出的五种方法C语言 输入10个数,统计出并输出正数、负数和0的个数字节三面:如何设计一个高并发系统架构,网络 面试36问,DDos攻击原理C++线程池使用 C++11 编写可复用多线程任务池

Bitmap 海量数据处理

阅读 : 1223

Bitmap:

说明:采用一个bit来标记某个value,可以大大节省存储空间。

优点是占用内存少,比如N为一亿(10000 0000),只需要N/8=1250 0000个byte,约等于12Mb。缺点为不能重复数据进行排序和查找

 

思想:利用一个byte中的8个bit来表示8个数。某数出现,利用映射将对应bit位置1。

比如元素3,在8bit的映射为

再来个元素5,在8bit的映射为

映射表为:

A[0]->0~7;

A[1]->8~15;

A[2]->16~23;

…….

 

具体实现:(例子为32位)

#include 
#define BITSPERWORD 32
#define SHIFT 5
#define MASK 0x1F
//#define N 10000000
#define N 100
 
int a[1 + N/BITSPERWORD];//申请内存的大小
 
void set(int i) {
//   a[i>>SHIFT] |=  (1<<(i& MASK));
   a[i/BITSPERWORD] |= (0x01<<(i%BITSPERWORD));
}
//clr 初始化所有的bit位为0
void clr(int i) {
//   a[i>>SHIFT] &= ~(1<<(i & MASK));
   a[i/BITSPERWORD] &= ~(0x01<<(i%BITSPERWORD));
}
//test 测试所在的bit为是否为1
int test(int i){
   return a[i>>SHIFT] & (0x01<<(i & MASK));
}
 
int main()
{  int i;
   for (i = 0; i < N; i++)
       clr(i);
   for(i=0;i

a[i/BITSPERWORD]或a[i>>SHIFT],实现i/32,即求出操作bit所在数的下标。

0x01<<(i%BITSPERWORD)或(1<<(i& MASK)),取得该操作bit的具体位置。   实例: 1、 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。 思路:8位数,0000 0000~9999 9999,范围为一亿。需要10^8个bit,12M左右字节。 实现:

int const nArraySize = 100000000 /(sizeof(int)*8) + 1;
int Bitmap[nArraySize];
void MapPhoneNumberToBit(int nNumber){
         Bitmap[nNumber/ (8*sizeof(int))] |= 0x00000001<<(nNumber%(8*sizeof(int)));
}

 

2、2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。

思路:用两个bit来表示一个数。0表示整数未出现,1表示出现一次,2表示出现多次。在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。注意控制与置位的分离。

 

实现

#include    
unsigned char flags[1000]; //数组大小自定义     
unsigned get_val(int idx)  {   
//  |    8 bit  |  
//  |00 00 00 00|  //映射3 2 1 0  
//  |00 00 00 00|  //表示7 6 5 4  
//  ……  
//  |00 00 00 00|  
  
    int i = idx/4;  //一个char 表示4个数,  
    int j = idx%4;    
    unsigned ret = (flags[i]&(0x3<<(2*j)))>>(2*j);    
    //0x3是0011 j的范围为0-3,因此0x3<<(2*j)范围为00000011到11000000 如idx=7 i=1 ,j=3 那么flags[1]&11000000, 得到的是|00 00 00 00|  
    //表示7 6 5 4  
   return ret;    
}    
        
unsigned set_val(int idx, unsigned int val)  {    
    int i = idx/4;    
    int j = idx%4;    
    unsigned tmp = (flags[i]&~((0x3<<(2*j))&0xff)) | (((val%4)<<(2*j))&0xff);    
    flags[i] = tmp;    
    return 0;    
}    
unsigned add_one(int idx)    
{    
    if (get_val(idx)>=2) {  //这一位置上已经出现过了??  
        return 1;    
    }  else  {    
        set_val(idx, get_val(idx)+1);    
        return 0;    
    }    
}    
//只测试非负数的情况;    
//假如考虑负数的话,需增加一个2-Bitmap数组.    
int a[]={1, 3, 5, 7, 9, 1, 3, 5, 7, 1, 3, 5,1, 3, 1,10,2,4,6,8,0};    
        
int main()   {    
    int i;    
    memset(flags, 0, sizeof(flags));    
            
    printf("原数组为:");    
    for(i=0;i < sizeof(a)/sizeof(int); ++i)  {    
        printf("%d  ", a[i]);    
        add_one(a[i]);    
    }    
    printf("\r\n");    
        
    printf("只出现过一次的数:");    
    for(i=0;i < 100; ++i)  {    
        if(get_val(i) == 1)    
            printf("%d  ", i);    
        }    
    printf("\r\n");    
      
    return 0;    
}  

 

3、给你一个文件,里面包含40亿个整数,写一个算法找出该文件中不包含的一个整数, 假设你有1GB内存可用。如果你只有10MB的内存呢?

思路:40亿个整数(40*10^8*4byte~16GB)。

A、 内存为1G

显然无法将16G的数据直接放入1G内存,采用bitmap算法,40*10^8bit= 0.5G(大约),满足1G的内存。

B、 内存为10M

如果我们只有10MB的内存,明显使用BitMap也无济于事了。 内存这么小,注定只能分块处理。比如分块的大小是1000,那么第1块的范围就是0到999,第2块的范围就是1000 到1999……

实际上并不保存这些数,而是给每一个块设置一个计数器。 这样每读入一个数,就在它所在的块对应的计数器加1。处理结束之后,我们找到一个块,它的计数器值小于块大小(1000),说明了这一段里面一定有数字是文件中所不包含的。然后我们单独处理这个块即可。