给定两个数组,编写一个函数来计算它们的交集。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
说明:
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
进阶:
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
思路:
法一:
和版本I的第2种方法差不多,在版本I的法2基础上去掉删除重复元素的步骤就可以
代码:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> rev;
if(nums1.empty() ||nums2.empty()) //如果数组其中一个为空,直接返回空数组
return rev;
sort(nums1.begin(),nums1.end()); //将数组1排序
sort(nums2.begin(),nums2.end()); //将数组2排序
vector<int>::iterator n1=nums1.begin(),n2=nums2.begin(); //设定指针n1和n2
while(n1<nums1.end() &&n2<nums2.end()) //在n1和n2不越界的条件下循环
{
if(*n1==*n2) //*n1==*n2的情况
{
rev.push_back(*n1);
n1++;
n2++;
}
while(*n1>*n2 && n2<nums2.end()) //*n1>*n2的情况
n2++;
while(*n1<*n2 &&n1<nums1.end() ) //*n1<*n2的情况
n1++;
}
return rev;
}
};
法二:
由于要考虑 nums1 的大小比 nums2 小很多的情况,所以假设在这样的情况下,我们可以直接对nums1的每个元素遍历nums2,这样最差需要花费(nums1.size()*nums2.size())的时间
法三:
也是考虑 nums1 的大小比 nums2 小很多的情况
用一个map存数组1中元素的个数,将元素作为key,元素的个数作为value
然后遍历数组2,如果数组2中元素的映射大于等于1,则将元素放到要返回的数组,并将映射减1
代码:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
map<int,int> m;
for(int i=0;i<nums1.size();i++) //遍历数组1,将数组1中对应元素的的映射加1
{
m[nums1[i]]++;
} //最后得到的映射就是数组1中每个元素的数量
vector<int> rev;
for(int i=0;i<nums2.size();i++) //遍历数组2
{
if(m[nums2[i]]>=1) //映射大于等于1的情况
{
rev.push_back(nums2[i]);
m[nums2[i]]--;
}
}
return rev;
}
};
如果给定的数组已经排好序呢?你将如何优化你的算法?
法二和法三没得优化
对于法一,则是增强了它的数据规模处理性,使得在不能一次加载所有的元素到内存中这种情况下法一也可行
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
法一在此情况下会有多余的排序,比如说nums1的长度是1,nums2的长度是1000000,那么会浪费排序的时间
法二在此情况下就比较不错了,因为nums1的长度小,所以综合不会花很多时间,但法二仅是适用于这种情况,在其它情况下就是蛮力法,是很糟糕的
法三无论是在正常情况,还是在上面这种极端情况,速度都比较快的
所以总的来说,在上面这种极端情况下,法二和法三更优
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
对于法一,它需要对所有元素进行排序,所以不适用
对于法二和法三,数据的先后顺序对它们影响不大,所以它们可以先从内存取一部分数据,再处理数据,再取数据再处理,直到数据处理完为止