Leetcode 350: 两个数组的交集 II
题目描述
给定两个数组,编写一个函数来计算它们的交集。
示例 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 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
解法1:我的方法和改进(利用map)
我的方法
把nums1容器的值作为键储存到哈希表中,但是nums1容器中会有相同的元素,然而对于map,不能存放相同键,可以这样处理:把元素出现次数作为键对应的值存进map中。然后遍历nums2容器,当nums2容器的值等于键时,判断此键对应的值是否为1,如果为1就删除这个键,不唯一就把对应的值减1。
代码见下:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> nums;
map<int, int> m;
for(int i=0; i<nums1.size(); i++){
if(m.count(nums1[i])){m[nums1[i]]++;} //如果键存在,值加一
else{m[nums1[i]]=1;} //否则键值为1
}
for(int i=0; i<nums2.size(); i++){
auto it = m.find(nums2[i]);
if(it != m.end()){
nums.push_back(nums2[i]);
if(m[nums2[i]] == 1){m.erase(it);}
else{m[nums2[i]]--;}
}
}
return nums;
}
};
改进
首先,可以加一个判断,对两个数组长度比较大小,将长度大的容器元素存储到map中,用长度小的容器进行遍历。
然后没必要使用迭代器,因为迭代器是为了得到此键的指针,然而本题并不需要用到指针信息,直接看键对应的值即可。
代码见下:
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
//if(nums1.size() < nums2.size()){swap(nums1,nums2);}
vector<int> nums;
map<int, int> m;
for(int i=0; i<nums1.size(); i++){
if(m.count(nums1[i])){m[nums1[i]]++;} //如果键存在,值加一
else{m[nums1[i]]=1;} //否则键值为1
}
for(int i=0; i<nums2.size(); i++){
if(m[nums2[i]] != 0){
nums.push_back(nums2[i]);
m[nums2[i]]--;
}
}
return nums;
}
};
然而。。。改进之后执行速度竟然不如改进之前快。。。
下面是对map的一个测试,以弄清map在没有添加键值对的时候值默认是否为0,以及修改为0之后键值对还存不存在。
int main()
{
map<int, int> m;
cout << "If no addition" << endl;
if (m.find(100) == m.end()) { cout << "No such key!\n"; }
else { cout << "Contains such key and the value is\n" << m[100]; }
cout << "Initialized value is " << m[100] << "\n" << endl;
cout << "After addition..." << endl;
m[100] = 1;
if (m.find(100) == m.end()) { cout << "No such key!" << endl; }
else { cout << "Contains such key and the value is " << m[100] << "\n" << endl; }
cout << "After changing the value..." << endl;
m[100]--;
if (m.find(100) == m.end()) { cout << "No such key!\n" << endl; }
else { cout << "Contains such key and the value is " << m[100] << "\n" << endl; }
return 0;
}
程序执行结果:
说明未添加键值对的时候,键是不存在的,但是不存在的键对应的值都初始化为了0。当添加了键值对之后键就存在了,此时把键所对应的值改为0之后键仍然存在,对应的值就是0。
解法2:暴力法(不推荐)
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> ret;
for(vector<int>::iterator it1 = nums1.begin(); it1 != nums1.end(); it1++) {
for(vector<int>::iterator it2 = nums2.begin(); it2 != nums2.end(); it2++) {
if(*it1 == *it2) {
ret.push_back(*it1);
*it1 = -10086;
*it2 = -10085;
}
}
}
return ret;
}
};
解法3:具有记忆的双指针法
先将两数组排序
一旦找到相同的元素,下一次查找位置不需要从头开始,直接从下一个位置查找
若未找到,此时nums1[i]>nums2[nums2.size()-1]大说明后面都没有相同的元素,反之从上一次搜索位置的下一个开始查找
static bool cmp(const int&a,const int &b)
{
return a<b;
}
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
if(nums1.empty()||nums2.empty())
return {};
sort(nums1.begin(),nums1.end(),cmp);
sort(nums2.begin(),nums2.end(),cmp);
if(nums1[0]>nums2[nums2.size()-1])
return {};
vector<int> res;
int i=0;//指向nums1的指针
int k=0;//指向nums2的指针
int j=0;//记忆上一次搜索位置
for(;i<nums1.size();i++)
{
while(k<nums2.size()&&nums1[i]!=nums2[k])//找到相同元素的nums2中的下标
k++;
if(k!=nums2.size())//找到相同元素
{
res.push_back(nums2[k]);
k++;
j=k;//记忆
}
else if(nums1[i]>nums2[k-1])//由于排序过,所以可以这样判断
break;
else k=j;//反之从上一次搜索位置开始
}
return res;
}
};
解法4:直接在数组上查找
class Solution {
public:
vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {
vector<int> res;
vector<int>::iterator it;
for(int i=0;i<nums1.size();i++)
{
it=find(nums2.begin(),nums2.end(),nums1[i]);
if(it!=nums2.end())//查找到元素
{
res.push_back(*it);
nums2.erase(it);//删除元素
}
}
return res;
}
};