[leetcode 350]. 两个数组的交集 II(解决百亿数据如何求交集)

两个数组的交集

  • 题目来源
  • 小数据问题的解决
    • 哈希表解决
    • 排序法
  • 进阶-num2数据在磁盘中
  • num1和num2数据都在磁盘中
    • int 类型的数据
    • 近似求结果的方式 - 布隆过滤器(string 类型数据)
    • 精确求结果 - 分治(string 类型数据)

题目来源

https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
[leetcode 350]. 两个数组的交集 II(解决百亿数据如何求交集)_第1张图片
单纯题目来看,如果没有进阶的第三个要求,这确实是一个简单的问题,我们先看看怎么解决前两问。

小数据问题的解决

在本题中,测试用例显然是小数据,我们就可以使用STL中的unordered_map的哈希表接口来解决了

哈希表解决

因为两个数组的大小不一定相等的,我们每一次尽量让哈希表中的数据是比较少的,这样在哈希表中查找的速率就尽可能变小一点。

  1. 先遍历第一个比较少量数据的数组,以此来建立哈希表,统计每个数据出现的次数
  2. 依次遍历第二个数组,每次判断该数据出现的次数,如果出现了,就对该结果-1,加入交集数组中
    //哈希表技术
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
        if(nums1.size() > nums2.size())
            return intersect(nums2,nums1);
        unordered_map<int,int> map;
        for(auto& n : nums1)
            map[n]++;
        
        vector<int> ans;
        for(auto& n : nums2)
            if(map[n] > 0) ans.push_back(n),map[n]--;
        return ans;
    }

排序法

我们先对两个数组进行排序,然后用双指针的方式对两个数组依次遍历。

  1. 两个位置的数据相等,两个指针同时后移
  2. 两个位置的数据不相等,比较小的数据的指针向后移
  3. 判断两个数组的指针是否越界,如果有一个越界,就说明查找结束
    //排序
    vector<int> intersect(vector<int>& nums1, vector<int>& nums2)
    {
        sort(nums1.begin(),nums1.end());
        sort(nums2.begin(),nums2.end());
        int le = 0,ri = 0;
        vector<int> ans;
        while(le < nums1.size() && ri < nums2.size())
        {
            if(nums1[le] == nums2[ri])
            {
                ans.push_back(nums2[ri]);
                le++;
                ri++;
                continue;
            }

            //nums1[le] != nums2[ri]
            if(nums1[le] < nums2[ri])
                le++;
            else
                ri++;
        }
        return ans;
    }

进阶-num2数据在磁盘中

这个时候,因为num2数组的数据在磁盘中,显然num1数组是可以在内存中的,所以我们采取的方法是第一个哈希映射的方式。

我们先对num1数组进行哈希映射,然后依次从磁盘中读取num2中的元素就可以。

因为num2的数据量比较大,所以我们可以在查找的时候,判断哈希表是否为空

  • 如果为空,就说明交集数据已经全部查找完毕,不需要继续再查找了,break
  • 如果不为空,则继续读取磁盘中num2的数据

num1和num2数据都在磁盘中

参考上一篇哈希位图与布隆过滤器

int 类型的数据

首先,因为磁盘中的数据过多,又因为int型数据最大只有42亿多,所以说磁盘中的数据是肯定存在重复的,所以说我们可以采用哈希位图的方式对num1数组中的数据建立索引。

这里有一个需要注意的点,就是如果我们用一个位(1和0)来表示一个数据是否存在的话,我们是无法知道这个数据出现了多少次,所以我们可以用多个位来表示一个数据是否出现。
[leetcode 350]. 两个数组的交集 II(解决百亿数据如何求交集)_第2张图片
道理都是一样的,选择用多少个位来表示一个数据的时候,可以根据具体情况而定

  • 如果选择的位数过多,那么很容易造成空间上的浪费
  • 如果选择的位数过少,那么又可能会造成无法统计完全的情况

这是对于数值类型的数据,我们采用位图的方式。那么对于字符串类型的数据,我们可以采用下面的方式:

近似求结果的方式 - 布隆过滤器(string 类型数据)

为什么是近似求解呢

因为布隆过滤器判断一个数是否出现,这个一个概率的结果

  • 判断一个数出现,可能会将结果进行误判。也就是存在将没有出现的数据,统计成了出现的。但是对于出现的数据,他是不会遗漏的
  • 判断一个数没有出现,那个这个数据是一定没有出现的

另外布隆过滤器不支持删除操作,防止误判

然后采用的方式也是方法一的哈希映射。不同的是,这里的哈希表变成了哈希位图,因为数据量很大

精确求结果 - 分治(string 类型数据)

对于精确算法,因为我们使用哈希位图映射的方式的时候,我们可以把那么多的数据根据不同的计算结果区分开,那么我们是否可以采用分治的算法呢?

[leetcode 350]. 两个数组的交集 II(解决百亿数据如何求交集)_第3张图片
我们采用这种方式,就可以把原本磁盘中很多的数据,分成同一个哈希地址的数据,将这些数据根据他们的哈希地址存储在一个磁盘中文件中

  • 这个时候,对于同一个文件中的数据,我们可以知道,他们的哈希地址都是相同的

然后对于num2在磁盘中的数据,我们依次进行遍历,然后计算哈希地址,依次在对应的文件中进行查找。
[leetcode 350]. 两个数组的交集 II(解决百亿数据如何求交集)_第4张图片
但是我们如果直接进行查找的话,在文件中的查找速率为O(n),这样下来,其实总体上还是一个O(n^2)的算法,这是不可取的。

所以说我们可以在文件中的数据放在一个set容器中,这样我们进行查找就是一个O(log n)的时间复杂度,因为set容器的底层是一个红黑树

你可能感兴趣的:(C++,题解)