[2016面试]海量数据处理

18 篇文章 0 订阅

互联网公司面试很喜欢问海量数据的查找(查找某个数,查找重复的数,查找未出现的数等等)、排序(全部排序、部分排序,找出第k大的数,找出前k大的数等等)等问题,基本的问题都是数据量很大(内存不够存放——降低空间复杂度)和性能要求高(运行时间有要求——减低空间复杂度)
这里列出一些常见的一些问题来处理

  1. 已知某个文件内包含一些电话号码,每个号码为8位数字,统计不同号码的个数。
    8位最多99 999 999,大概需要99m个bit,大概10几m字节的内存即可。 (可以理解为从0-99 999 999的数字,每个数字对应一个Bit位,所以只需要99M个Bit==1.2MBytes,这样,就用了小小的1.2M左右的内存表示了所有的8位数的电话)

  2. 2.5亿个整数中找出不重复的整数的个数,内存空间不足以容纳这2.5亿个整数。
    将bit-map扩展一下,用2bit表示一个数即可,0表示未出现,1表示出现一次,2表示出现2次及以上,在遍历这些数的时候,如果对应位置的值是0,则将其置为1;如果是1,将其置为2;如果是2,则保持不变。或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map,都是一样的道理。

  3. 给你A,B两个文件,各存放50亿条URL,每条URL占用64字节,内存限制是4G,让你找出A,B文件共同的URL。如果是三个乃至n个文件呢?
    解决方案:
    将数据文件分割为20个文件,然后将每个文件内的url进行hashcode计算,然后存入长度为该hashcode结果值最大值得长度的BitSet中,例如hashcode最大值为99999999,那么bitset的大小就应该是9千多万位,实际上bitset大小最多可容纳2的32次方位,即4294967296,40多亿,如果存在此Hashcode则为1,否则为0,最后将所有位为1的数据取出来就去重了。

此为单bitset想法,而布隆算法其实就是多个bitset,多个hash,防止冲突而已

  1. 假设要你写一个网络蜘蛛(web crawler)。由于网络间的链接错综复杂,蜘蛛在网络间爬行很可能会形成“环”。为了避免形成“环”,就需要知道蜘蛛已经访问过那些URL。给一个URL,怎样知道蜘蛛是否已经访问过呢?

简单的来说就是大数据量文件的查找或者去重类似这样的场景

c++中bitset的一些操作
bitset b; b有n位,每一位默认为false(0),
bitset b(s,pos,m,zero, one); b是string s从pos开始m个字符的拷贝,string中的字符只能是zero或one

public void set(int pos): 位置pos的字位设置为true。
public void set(int bitIndex, boolean value) 将指定索引处的位设置为指定的值。
public void clear(int pos): 位置pos的字位设置为false。
public void clear() : 将此 BitSet 中的所有位设置为 false。
public int cardinality() 返回此 BitSet 中设置为 true 的位数。

bitset排序(关键,元素不能重复)

我们要对0-7内的五个元素进行排序, 假设这5个元素为(4, 7, 2, 5, 3)。 假设元素没有重复的。 要表示8个数, 我们需要8个bit。也就是1byte.

下面我们只需要开辟1byte的空间, 对这个1byte的所有的bit都初始化为0。

如下: 0000 0000

我们可以规定, 有如下的map。

0000 0000 —> 0

0000 0010 —> 1

0000 0100 —> 2

0000 1000 —> 3
0001 0000 —> 4
0010 0000 —> 5
0100 0000 —> 6
1000 0000 —> 7
以上的map是我们默认的, 不需要去另外开辟空间存这个映射表格(不过我们也可以吧index设为从1开始, 就有如下映射:

0000 0001 —-> 1

0000 0010 —-> 2
………………………..

1000 0000 ——-> 8

)

试着想想, 如果我们直接存放, 需要 8 x sizeof(int) = 32byte。

试着想想, 如果我们直接存放, 需要 8 x sizeof(int) = 32byte。

但是由于我们使用了bitmap, 我们只需要8 x 1 bit = 1byte既可以。 好节省空间啊。 节省32倍。 太牛了。

下面我们就应用这一个byte存储着5个元素, 得到的存储信息如下:

首先, 输入第一个元素4, 对应如下:

0001 000.

第二个元素输入是7, 所以将第8位设置为1, 变成如下:

1001 0000

最终, 如下:

1011 1100代表着所有的元素4, 7, 2, 5, 3。

然后我们遍历一遍bit区域, 将改为编号是1的编号(索引)输出, 就是(2, 3, 4, 5, 7)。

#include <bitset>
#include <iostream>

using namespace std;

int main() {
    int arr[5] = {4, 7, 2, 5, 3};
    bitset<8> mybit; // default constructor all to 0

    for(int i = 0; i < 5; ++i) {
        mybit.set(arr[i]); // set bit arr[i], 即置1
    }

    cout << mybit << endl;

    cout << "sorting using bitmap: ";
    for(int i = 0; i < 8; ++i) {
        if(mybit[i]) {
            cout << i << " ";
        }
    }
}

下面一个非常重要的问题是如何自己实现bitmap

从上面的描述可以看到,如果数的范围是0~31
则 bitset<32> mybit; 每次读入的数据i需要将
mybit.set; //将第i位设为1,i的范围是[0,31]

这样占用的内存大小是N bit即N/8 byte即4个字节

这里我们用int型的数组来表示bitmap,数组的每一个元素都是int型,32bit,所以一共需要的数组长度为N/32+1
对于某个数i = 32*a+b的形式,那么我们应该将bitmap的第a个位置元素的第b位置为1

上面有几个关键操作
如何求出a,a相当于是i除以32的商,可以i/32或者利用位运算i>>5右移5位
如何求出b, b相当于i除以32的余数,可以i%32或者i&0x1F()x1F表示的是16进制中的31)
这里来说明下 一个数除以8的余数相当于 它与0111(7的二进制)作与操作。
如何将第a个元素的b位置置为1,只需要将1左移b位再和a作或操作即可。

#define WORD 32
#define SHIFT 5 移动5个位,左移则相当于乘以32,右移相当于除以32取整
#define MASK 0x1F //16进制下的31
#define N 10000000
int bitmap[1 + N / WORD];
/*
 * 置位函数——用"|"操作符,i&MASK相当于mod操作
 * m mod n 运算,当n = 2的X次幂的时候,m mod n = m&(n-1)
 */
void set(int i) {
    bitmap[i >> SHIFT] |= (1 << (i & MASK));
}
/* 清除位操作,用&~操作符 */
void clear(int i) {
    bitmap[i >> SHIFT] &= ~(1 << (i & MASK));
}
/* 测试位操作用&操作符 */
int test(int i) {
    return bitmap[i >> SHIFT] & (1 << (i & MASK));
}
int main(void) {
    FILE *in = fopen("in.txt", "r");
    FILE *out = fopen("out.txt", "w");
    if (in == NULL || out == NULL) {
        exit(-1);
    }
    int i = 0;
    int m;
    for (i = 0; i < N; i++) {
        clear(i);
    }
    while (!feof(in)) {
        fscanf(in, "%d", &m);
        printf("%d/n", m);
        set(m);
    }
    printf("abnother");
    for (i = 0; i < N; i++) {
        if (test(i)) {
            printf("%d/n", i);
            fprintf(out, "%d/n", i);
        }
    }
    fclose(in);
    fclose(out);
    return EXIT_SUCCESS;
}

http://blog.csdn.net/qibaoyuan/article/details/5914746

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值