海量数据问题汇总

 bitset结构解海量数据的问题。

位图是内存中连续二进制bit,用于对海量数据的去重和查询。
char是一个字节,8个位。如果要把整数10映射到位图,需要找到第二个char数据,然后再找到第二个char的第二个bit。
1G=1024M 1M=1024KB
bitset存储二进制位
bitset就像一个bool类型的数组一样,但是有空间优化
bitset中的一个元素一般只占1bit,相当于一个char元素所占空间的八分之一
bitset中的每个元素都能被单独访问,例如对于一个叫foo的bitset,表达式foo[3]访问它的第4个元素
整数类型和布尔数组都能转化成bitset。
其实和vector<bool>没有区别啊。

复制代码
给定1亿个整数,设计算法找到只出现一次的整数。
定义两个位图。
一次都没有出现,在两个位图中都表示0;
只出现一次,在位图一标记位1,在位图二标记为0
出现过两次及两次以上的整数,在位图一标记为1,位图二标记为1

  一个文件有100亿个int,1G内存,设计算法找到出现次数不超过2次
  使用两个位图,分别是位图1和位图2
  数据出现0次,两个位图都记录为0
  1次,位图1中记录1,位图2记录为0
  2次,位图1中记录0,位图2记录为1
  3次及三次以上,位图1和2都记录1

复制代码

 

美团第三题,给定两个数组,每个元素表示文件被修改第N次的范围,求同时被修改的文件号
(和下面的区别就在于这里会重复遍历一个文件)
设置两个bitset,首先遍历第一个改变的,不改变bitset1是0,改变bitset1是1
然后遍历第二个改变的,如果bitset1是1,并且bitset2是0,加入并且把bitset2置为1
如果bitset1是1,并且bitset2是1,不动。
但是ACM模式输入的不能是常量啊,所以只能使用vector<bool>来取代这个结构哦。

 

给定两个文件,分别有100亿个整数,如何找到两个文件的交集
将第一个文件中的所有数据映射到位图,如果存在则为1,不存在则为0
然后将第二个文件中的所有数据和位图进行对比,如果对比到的位置是1,说明该数是两个整数的交集
然后将该交集的数据放在第三个文件中

 

布隆过滤器解海量数据问题。

用多个哈希函数,将一个数据映射到位图结构中。这种方法不仅可以提升查询效率,也可以节省大量的空间。

布隆过滤器主要是把字符串等其他数据映射到位图中,但是位图只能映射整数,所以我们需要通过哈希函数把字符串变量转换成整数,然后映射到位图中。但是不同的字符串利用哈希函数转换为整数有可能冲突,导致不同字符串映射到位图的相同位置。为了减少这种冲突,可以利用不同的哈希函数将字符串转换为不同的整数,再将转换的整数都映射到位图里面。例如,“张三”转换为20、45、89,然后将转换的这三个整数都映射到位图中。如果要判断张三这个字符串是否存在,就需要判断这3个整数是否都存在,如果其中一个整数不存在,则该字符串就不存在。

复制代码
#include <bitset>
#include <iostream>
#include <vector>
using namespace std;

template<size_t N>
class BitSet
{
private:
    vector<char> v;//一个v
public:
    BitSet()
    {
        v, resize(N / 8 + 1);//开始N个bit
    }
    void Set(size_t x)//将x映射到位图当中
    {
        //找相对应的位图下标
        int index = x / 8 + 1;//x在位图中的第几个char中
        int place = x % 8;
        v[index] |= (1 << place);
    }
    void ReSet(size_t x)//删除x在位图中的映射
    {
        int index = x / 8 + 1;//x在位图中的第几个char中
        int place = x % 8;
        v[index] &= (~(1 << place));
    }
    bool Test(size_t x) //判断一个数据是不是在位图中
    {
        //找到相对应的位图下表
        int index = x / 8 + 1;
        int place = x % 8;
        return v[index] & (1 << place);
    }
};

struct BKDRHash
{
    BKDRHash() { }
    size_t operator()(const string& str)
    {
        size_t hash = 0;
        for (auto ch : str)
        {
            hash = hash * 131 + ch;
        }
        return hash;
    }
};

struct APHash
{
    APHash() { }
    size_t operator() (const string str)
    {
        register size_t hash = 0;
        size_t ch;
        for (long i = 0; i < str.size(); i++)
        {
            ch = str[i];
            if ((i & 1) == 0) hash ^= ((hash << 7) ^ ch ^ (hash >> 3));
            else hash ^= (~((hash << 11) ^ ch ^ (hash >> 5)));
        }
        return hash;
    }
};

struct DJBHash
{
    DJBHash() { }
    size_t operator()(const string& str)
    {
        size_t hash = 5381;
        for (auto ch : str)
        {
            hash += (hash << 5) + ch;
        }
        return hash;
    }
};

template<size_t N, class K = string,
class Hash1 = BKDRHash,  
class Hash2 = APHash,
class Hash3 = DJBHash>
class BloomFilter
{
private:
    BitSet<N> _bitset;
public:
    void Set(const K& s)//对字符串建立映射
    {
        size_t i1 = Hash1()(s) % N;
        size_t i2 = Hash2()(s) % N;
        size_t i3 = Hash3()(s) % N;
        _bitset.Set(i1);
        _bitset.Set(i2);
        _bitset.Set(i3);
    }
    bool Test(const K& s)
    {
        size_t i1 = Hash1()(s) % N;
        size_t i2 = Hash2()(s) % N;
        size_t i3 = Hash3()(s) % N;
        if (!_bitset.Test(i1)) return false;
        if (!_bitset.Test(i2)) return false;
        if (!_bitset.Test(i3)) return false;
        return true;
    }
};
复制代码

 

TopK问题:分治(哈希算法)+HashMap/Trie树+小顶堆或大顶堆。
(1)有一个1g大小的文件,里面的每一行都是一个词,词的大小不超过16字节,内存限制大小1M。返回频数最高的100词。

顺序读取文件每一行,利用哈希算法映射到2000个小文件。对每个小文件,使用trie树统计每个词的词频。使用小顶堆计算每个小文件的top100。使用归并,计算2000和top100的top100。
(2)海量日志数据,提取某日访问百度次数最多的ip。

IP是32位,使用哈希算法存到1000个小文件里面。使用HashMap统计频率,找到最大的频率。使用小顶堆计算1000个文件里面的最大频率对应的IP。

(3)海量数据分布在100台电脑,高效统计出TOP10。

虽然数据是分布的,但相同数据可能在不同电脑。因此有些总数属于TOP10的,可能在某台机器的分布只排TOP11。

因此应该使用哈希算法进行电脑重分配。然后使用HashMap统计每台电脑数据频率。然后使用最小堆在每台电脑求TOP10。

然后组合100台机器的TOP10,再使用最小堆求TOP10。

(4)一个有一万行单词的文本文件,要求统计出其中最频繁出现的前10个词。

一万行不算多,不用分割文件。然后使用Trie树统计词的频数。然后使用小顶堆求出TOP10。如果行数多的话就要增加一个步骤:使用哈希算法分割成小文件。再对每个小文件循环上面操作。

(5)一百万个数找出最大的100个。像刷题一样,可以使用快排、堆排。堆排的效率比较高。

 

重复问题:bitmap位图/布隆过滤器/哈希集合,每个元素对应一个位处理。

(1)给定a/b两个文件,各自存放50亿个URL地址,每个地址各占64个字节,内存限制是4G,找出两个文件公共的URL。

方案一:每个文件的大小大概是5G*64=320G,远远超过内存大小,考虑分而治之的方法。

可以先遍历文件a,使用哈希算法分散到1000个小文件,文件b使用同样的哈希算法分散到1000个小文件。只有相同编号的小文件才可能有相同的元素。接下来,读取a文件某编号的所有URL到Hash_map,然后遍历b中同样编号的所有URL,看是不是再那个Hash_map里面。如果存在,输出文件。

方案二:如果允许有一定错误率,可以使用布隆过滤器,使用位数组。

4G内存大概可以表示340亿bit。将其中一个文件的URL使用布隆过滤器映射为这340亿个位,然后挨个读取另外一个文件的URL。检查是不是在布隆过滤器。如果是,那么该URL应该是共同的(有一定错误率)。

(2)在2.5亿个整数中找出不重复的整数,内存不足以容纳这2.5亿个整数。

使用哈希算法分割成小文件。然后利用Hash_set,在小文件找出不重复的整数,再进行归并。

(3)一个文件包含40亿个整数,找出不包含的一个整数。

对于32位的整数,一共有2^32个,每个数对应一个bit,一共需要0.5GB的内存。遍历文件,将每个数对应的位置为1,最后查找位为0的拼凑起来就可以。


排序问题:外排序/bitmap位图。分割文件+文件内排序+文件间归并。

 (1)有10个文件,每个文件1G,每个文件的每一行存放的都是用户的query,每个文件的query都可能重复。要求按照query的频度进行排序。(不是像TOPK问题,只要得到TOPK的那种堆只要固定几个就好的解法,是要全部都排序)

首先进行分治,顺序读取每一行,使用哈希算法将它们全部都重新分配到十个文件。使用Hash_map记录(query和query_count),调入内存,利用快速排序/堆排序/归并排序等内部排序算法进行排序,输出到十个文件。再对十个文件进行归并排序。

 

posted @   花与不易  阅读(62)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示