实现一个位图、100亿个整数找出只出现一次的整数、找出现次数不超多2次的数

本文介绍位图的基本概念和实现方式,并针对特定场景提出高效解决方案,包括寻找唯一整数、文件交集等问题。此外,还探讨了布隆过滤器的扩展应用,以及字典树在字符串匹配中的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.–实现一个位图。

位图特点:
节省空间 适合数据比较密集的情况 适合判断事件 是否 这样的问题
相关位运操作: & | ~

代码实现:

#include<iostream>
#include<vector>

using namespace std;

//
//1.【基础题】--实现一个位图。
//位图特点: 节省空间,适合数据密集的情况


class BitSet
{
public:
    BitSet()
        :_size(0)
    {
    }

    BitSet(size_t size)
    {
        _size = (size >> 5) + 1;
        _array.resize(_size);
    }

    bool Set(int num)
    {
        //定位到位置  进行置1
        //比特位操作  
        //位运算: & |  

        //找到对应字节,将其对应比特位进行位运算

        int index = num >> 5;
        if (index >= _size)
        {
            return false;
        }
        int bit = num % 32;  //该位置进行置1  ,
        _array[index] =_array[index] |(1<<(31-bit));
        return true;

    }
    bool ReSet(int num)
    {
        //找到对应字节,将其对应比特位进行位运算
        int index = num >> 5;
        if (index >= _size)
        {
            return false;
        }
        int bit = num % 32;  //该位置进行置0,其他位保持不变
        _array[index] = _array[index] &(~(1 << (31 - bit)));
        return true;
    }

    bool Test(int num)
    {
        int index = num >> 5;
        int bit = num % 32; 
        if (_array[index] & (1 << (31 - bit)))
        {
            return true;
        }
        else
        {
            return false;
        }
    }

private:
    vector<int >  _array;
    size_t _size;
};

2.以下三个问题都是位图相关类似题目

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

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

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

大家也可以尽可能多的发散考虑其他可行的思路

1)给一个超过100G大小的log file,log中存着IP地址,设计算法找到出现次数最多的IP地址?(与如何知道top K的IP,如何使用Linux系统命令实现)

  Hash分桶法:

  将100G文件分成1000份,将每个IP地址映射到相应文件中:file_id = hash(ip) % 1000

  在每个文件中分别求出最高频的IP,再合并Hash分桶法;

  使用Hash分桶法把数据分发到不同的文件;

  各个文件分别统计top K;

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

  Hash分桶法,将100亿个整数映射到不同的区间,在每个区间中分别找只出现一次的整数。

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

  扫描每个整数是否出现过,节省内存方法使用bitmap。桶分 + bitmap。如果整数是32bit,直接使用bitmap的方法实现。所有整数共2^32种可能,每个数用两位表   示,00表示文件均没出现,10表示文件1出现过,01表示文件2出现过,11表示两文件均出现过,共需要2^32*2/8 = 1GB内存,遍历两个文件中的所有整数,然后寻  找bitmap中11对应的整数即是两个文件的交集,这样即可线性时间复杂度完成。

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

  Bitmap扩展:用2个bit表示状态,0表示未出现,1出现过1次,2出现过2次或以上。

5)给两个文件,分别有100亿个query,我们只有1G内存,如何找到两个文件交集?分别给出精确算法和近似算法?

  精确算法:Hash分桶法

  将两个文件中的query hash到N个小文件中,并标明query的来源;

  在各个小文件中找到重合的query

  将找到的重合query汇总

  近似算法:BloomFilter算法

6)如何扩展BloomFilter使得它支持删除元素的操作

  将BloomFilter中的每一位扩展为一个计数器,记录有多少个hash函数映射到这一位;删除的时候,只有当引用计数变为0时,才真正将该位置为0。

7)如何扩展BloomFilter使得它支持计数操作?

  将BloomFilter中的每一位扩展为一个计数器,每个输入元素都要把对应位置加1,从而支持计数操作。计数个数为所有映射到的位置计数的最小值。

8)给上千个文件,每个文件大小为1K-100M。给n个词,设计算法对每个词找到所有包含它的文件,你只有100K内存。

0: 用一个文件info 准备用来保存n个词和包含其的文件信息。
1 : 首先把n个词分成x份。对每一份用生成一个布隆过滤器(因为对n个词只生成一个布隆过滤器,内存可能不够用)。把生成的所有布隆过滤器存入外存的一个文件Filter中。
2:将内存分为两块缓冲区,一块用于每次读入一个布隆过滤器,一个用于读文件(读文件这个缓冲区使用相当于有界生产者消费者问题模型来实现同步),大文件可以分为更小的文件,但需要存储大文件的标示信息(如这个小文件是哪个大文件的)。
3:对读入的每一个单词用内存中的布隆过滤器来判断是否包含这个值,如果不包含,从Filter文件中读取下一个布隆过滤器到内存,直到包含或遍历完所有布隆过滤器。如果包含,更新info 文件。直到处理完所有数据。删除Filter文件。

备注:
1:关于布隆过滤器:其实就是一张用来存储字符串hash值的BitMap.
2:可能还有一些细节问题,如重复的字符串导致的重复计算等要考虑一下。

9)一个词典,包含N个英文单词,现在任意给一个字符串,设计算法找出包含这个字符串的所有英文单词。

  给输入字符串,利用字母建立倒排索引,索引中存储该字母出现在哪个单词以及在单词中位置;查询时,利用倒排找到所有单词,并求交集并且位置要连续。

9)有一个词典,包含N个英文单词,现在任意给一个字符串,设计算法找出包含这个字符串的所有英文单词。

答:

对于这道题目,我们要用到一种特殊的数据结构—-字典树来解决它,所谓字典树,又称单词查找树(或Trie树),是一种哈希树的变种。

典型应用:用于统计、排序和保存大量的字符串,经 常被搜索引擎系统用于文本词频统计。

优点:利用字符串的公共前缀来减少查询时间,最大 限度地减少无谓的字符串比较,查询效率高于哈希表 。

基本性质:根节点不包含字符,除根节点外每个节点 都只包含一个字符;

从根节点到某一节点,路径上所有经过的字符连接起来,为该节点对应的字符串;

每个节点的所有子节点包含的字符都不相同 。

应用:串的快速检索、串排序、最长公共前缀
  
参考于:http://www.cnblogs.com/qingjiaowoxiaoxioashou/p/6042571.html

<think>我们面对的是一个大数据集的高效查问题。根据引用内容,我们可以提取几个关键点:1.引用[2]展示了使用bitset处理大数据的方法,但注意bitset适用于稠密整数集且需要大量连续内存。2.引用[3]展示了二分查算法,适用于有序据的查。3.用户需求是“处理非常大的据集”并实现“快速查”,因此我们需要推荐适合大数据集的查算法,并用C++实现。考虑大数据集的特点:-据量非常大,可能无法完全装入内存(但根据引用[2]的例子,用户似乎可以接受在内存中处理,因为使用了7亿大小的bitset)-需要高效查,通常考虑时间复杂度常见的高效查算法:1.哈希表(unordered_map,unordered_set):O(1)平均时间复杂度,但需要较大内存,且保证有序。2.二叉搜索树(map,set):O(logn)时间复杂度,有序存储。3.二分查:要求有序组,O(logn)时间复杂度,但插入删除代价高,适合静态据。4.位图(bitset):如引用[2],适用于整数且范围有限的情况,空间效率高(每个位代表一个),但只能处理整数且范围能太大。5.布隆过滤器:概率型据结构,适用于判断元素是否存在(可能有误判),节省空间。根据用户场景择:-如果据集是整数且范围相对集中(比如0到10亿),bitset是很好的择(空间效率极高,查O(1))。-如果据集是整数但范围很大,可以考虑布隆过滤器(存在误判)或哈希表(需要较内存)。-如果据集是字符串或复杂类型,则哈希表或二叉搜索树更合适。-如果据是有序的且静态(很少更新),二分查非常高效(如引用[3]求平方根,其实也是二分查的应用)。另外,对于特别大的据集(出内存),我们需要考虑外部存储的查结构,如B树(据库常用)或LSM树(Log-StructuredMerge-Tree),但用户要求用C++实现,且引用中未涉及外部存储,我们暂考虑。因此,我们给出以下推荐:###推荐算法及C++实现####1.位图(Bitset)-适用于整数据集,且整数范围已知且集中如引用[2]所示,bitset可以高效地表示整数集合,并支持快速查(设置位表示存在)。但注意bitset的大小需要提前确定,且如果整数范围很大(例如几十亿)但实际据稀疏,则会造成空间浪费。示例代码(统计某个范围内数字出现的个):```cpp#include<iostream>#include<bitset>#include<vector>usingnamespacestd;intmain(){//假设整数范围在0到MAX_VALUE-1constsize_tMAX_VALUE=1000000000;//10亿bitset<MAX_VALUE>*bitmap=newbitset<MAX_VALUE>();//填充据(示例:将100200000000之间的置为1)for(longlongi=100;i<200000000;i++){if(i<MAX_VALUE){bitmap->set(i,1);}}//查:统计从99999到90000000之间存在的数字longlongsum=0;for(longlongi=99999;i<90000000;i++){//注意:这里循环条件似乎有误,起始大于结束,实际应该调整//正确示例:假设我们统计100000到200000000//但为了演示,我们改为从0到1000000//实际应用请根据需求调整if(i<MAX_VALUE&&bitmap->test(i)){sum++;}}cout<<"Count:"<<sum<<endl;deletebitmap;return0;}```注意:上述循环范围错误(99999到90000000,起始大于结束),实际使用时请确保范围正确。####2.哈希表(unordered_set)-适用于任意类型,内存充足时如果据是任意类型(如字符串)或整数范围很大但据稀疏,使用哈希表更节省内存(相对于大范围bitset)且查快速。示例代码:```cpp#include<iostream>#include<unordered_set>#include<vector>usingnamespacestd;intmain(){unordered_set<longlong>data_set;//填充据for(longlongi=100;i<200000000;i++){data_set.insert(i);}//查:统计在某个范围内出现在集合中的数字longlongsum=0;for(longlongi=100000;i<200000000;i++){//假设我们要统计这个范围if(data_set.find(i)!=data_set.end()){sum++;}}cout<<"Count:"<<sum<<endl;return0;}```####3.二分查-适用于静态有序据集如果据集是有序的,并且需要频繁更新,可以使用二分查。首先将据排序(如果未排序),然后使用二分查。示例代码(整数组):```cpp#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;intmain(){vector<longlong>data;//假设填充有序据(或先排序)for(longlongi=100;i<200000000;i++){data.push_back(i);}//如果无序,需要先排序(这里假设已经有序)//sort(data.begin(),data.end());//查某个值是否存在,并统计在某个范围内的据个(注意:二分查单个值,统计范围需要其他方法)//二分查单个值示例:longlongtarget=123456;boolfound=binary_search(data.begin(),data.end(),target);cout<<"Is"<<target<<"exists?"<<(found?"Yes":"No")<<endl;//如果要统计有序组中在某个区间内的元素个,可以使用二分查确定上下界autolower=lower_bound(data.begin(),data.end(),100000);//第一个>=100000的位置autoupper=upper_bound(data.begin(),data.end(),200000);//第一个>200000的位置longlongcount=distance(lower,upper);//[100000,200000]左闭右开区间,实际包含100000到200000(包含等于200000?注意upper_bound是大于,所以包含200000?)//注意:区间[100000,200000]内的元素个:从第一个>=100000到第一个>200000之间的元素个cout<<"Countin[100000,200000]:"<<count<<endl;return0;}```####4.布隆过滤器-适用于判断存在性且允许一定误判当据集非常大,且内存紧张时,可以使用布隆过滤器。但注意,它只能判断元素“可能存在”或“一定存在”。示例代码(需要第三方库,如SimpleBloomFilter,或自己实现):```cpp//这里给出一个简单的布隆过滤器实现示例(需要个哈希函)#include<iostream>#include<bitset>#include<functional>#include<vector>usingnamespacestd;classBloomFilter{private:bitset<10000000>bits;//位组大小,根据需求调整vector<function<size_t(string)>>hash_functions;//字符串哈希函public:voidadd_hash_function(function<size_t(string)>func){hash_functions.push_back(func);}voidinsert(conststring&key){for(auto&hash_func:hash_functions){size_thash_val=hash_func(key)%bits.size();bits.set(hash_val);}}boolmay_contain(conststring&key){for(auto&hash_func:hash_functions){size_thash_val=hash_func(key)%bits.size();if(!bits.test(hash_val)){returnfalse;}}returntrue;}};//示例哈希函1size_thash1(conststring&s){hash<string>str_hash;returnstr_hash(s);}//示例哈希函2(简单移位哈希)size_thash2(conststring&s){size_th=0;for(charc:s){h=(h<<5)|(h>>27);//循环移位h+=c;}returnh;}intmain(){BloomFilterbf;bf.add_hash_function(hash1);bf.add_hash_function(hash2);//插入元素bf.insert("hello");bf.insert("world");//查询cout<<"hello:"<<bf.may_contain("hello")<<endl;//1cout<<"world:"<<bf.may_contain("world")<<endl;//1cout<<"test:"<<bf.may_contain("test")<<endl;//0(可能误判为1)return0;}```###总结推荐-**整数且范围集中**:优先择`bitset`,空间效率最高,查O(1)。-**整数或字符串,内存充足**:择`unordered_set`(哈希表),查O(1)平均。-**静态有序据**:使用`二分查`(通过`binary_search`,`lower_bound`,`upper_bound`),查O(logn)。-**内存紧张且允许误判**:择`布隆过滤器`,节省空间,但存在误判。请根据据集的具体特点(据类型、范围、是否有序、内存限制等)择合适的算法。§§相关问题§§1.如何根据据集的大小和特点择最适合的查算法?2.在C++中,bitset和布隆过滤器有什么区别?3.如何优化哈希表以减少冲突提高查效率?4.对于动态变化的大数据集,应该择什么查结构?</think>### C++处理大数据集的高效查算法推荐 在处理大数据集时,查算法的择需考虑**时间复杂度**、**内存占用**和**据特性**。以下是针对同场景的推荐算法及实现方法: --- #### 1. **位图法 (Bitset)** **适用场景**:整数据集,据范围已知且相对集中(如0-10亿) **优势**:$O(1)$查复杂度,极致空间效率(1bit/据) **实现示例**(统计特定范围内数字出现次数): ```cpp #include <bitset> #include <iostream> constexpr size_t MAX_RANGE = 200000000; // 2亿范围 int main() { auto bitmap = std::make_unique<std::bitset<MAX_RANGE>>(); // 填充据 (示例:标记100-150000000) for (long i = 100; i < 150000000; ++i) { bitmap->set(i, true); } // 统计90000000-100000000的出现次数 size_t count = 0; for (long i = 90000000; i < 100000000; ++i) { if (bitmap->test(i)) ++count; } std::cout << "Count: " << count << std::endl; return 0; } ``` > **适用性**:适合IP过滤、用户ID验证等场景[^2]。 --- #### 2. **哈希表 (Hash Table)** **适用场景**:任意据类型,需快速精确查 **优势**:平均$O(1)$查复杂度,支持动态更新 **实现示例**: ```cpp #include <unordered_set> #include <vector> int main() { std::unordered_set<long> data_set; // 插入据 (示例插入1亿条) for (long i = 0; i < 100000000; ++i) { data_set.insert(i * 2); // 存入偶 } // 快速查 long target = 12345678; if (data_set.find(target) != data_set.end()) { std::cout << target << " exists!" << std::endl; } return 0; } ``` > **优化建议**: > - 预分配空间:`data_set.reserve(100000000);` > - 自定义哈希函避免碰撞 --- #### 3. **二分查 (Binary Search)** **适用场景**:**静态有序据集** **优势**:$O(\log n)$时间复杂度,内存友好 **实现示例**(参考平方根算法[^3]): ```cpp #include <algorithm> #include <vector> // 在有序大数据集中查 bool binary_search(const std::vector<long>& data, long target) { auto it = std::lower_bound(data.begin(), data.end(), target); return (it != data.end() && *it == target); } int main() { std::vector<long> big_data(100000000); // 填充有序据(已排序) std::iota(big_data.begin(), big_data.end(), 0); long target = 56789012; if (binary_search(big_data, target)) { std::cout << "Found: " << target << std::endl; } return 0; } ``` --- #### 4. **布隆过滤器 (Bloom Filter)** **适用场景**:海量据存在性检查,允许少量误判 **优势**:空间效率极高(1字节/元素),$O(k)$查复杂度 **实现示例**: ```cpp #include <bitset> #include <functional> class BloomFilter { std::bitset<10000000> bits; // 1千万位容器 public: void add(const std::string& key) { size_t h1 = std::hash<std::string>{}(key) % bits.size(); size_t h2 = std::hash<size_t>{}(h1) % bits.size(); bits.set(h1); bits.set(h2); } bool may_contain(const std::string& key) { size_t h1 = std::hash<std::string>{}(key) % bits.size(); size_t h2 = std::hash<size_t>{}(h1) % bits.size(); return bits.test(h1) && bits.test(h2); } }; ``` > **适用场景**:垃圾邮件过滤、爬虫URL去重[^1]。 --- ### 算法择决策树 1. **据类型是整数且范围有限?** → **Bitset** 2. **需要支持动态更新?** → **Hash Table** 3. **据静态有序?** → **Binary Search** 4. **允许少量误判且内存紧张?** → **Bloom Filter** 5. **据量内存限制?** → 需结合外部排序+B树 > **性能对比表**: > | 算法 | 时间复杂度 | 空间复杂度 | 是否精确 | > |---------------|------------|------------|----------| > | Bitset | $O(1)$ | $O(n)$ | ✓ | > | Hash Table | $O(1)$ | $O(n)$ | ✓ | > | Binary Search | $O(\log n)$| $O(1)$ | ✓ | > | Bloom Filter | $O(k)$ | $O(1)$ | ✗ | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值