解题思路
1.题目描述
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例1:
示例2:
2.题目分析
2.1 通过哈希集合解题
- 我们这里要计算的是两个数组的交集,最容易想到的的方法就是先遍历数组
nums1
,然后再查询其中的每个元素是否存在于nums2
中,如果存在,则将该元素添加到返回值。 - 这样我们很容易就会想到使用一个辅助数组存储已经在
nums1
遍历到的值,然后再查看在nums2中的元素是否已经存在于辅助数组,若存在则将该元素添加到结果数组。但是我们会面临一个问题,那就是这样用辅助数组进行存储的话,不仅数组的大小无法确定,而且数组的值十分分散且不容易查找,这就会造成 空间的极大浪费和操作的不便。 - 所以我们需要一种可以快速进行确定某个元素是否存在、大小不受限制的数据结构进行存储辅助数组,这样我们就想到了使用
unordered_set
来存储辅助数据。
这样我们就得出以下解题步骤
- 首先初始化一个名为
uSet
的unordered_set
来进行存储在nums1中已经遍历到的值。 - 遍历nums1,每当遍历到一个元素就将其添加到
uSet
中 - 遍历nums2,每当遍历到一个元素就查看其是否在
uSet
中存在,若存在则将其存储到另一个结果unordered_set
中; - 用结果
unordered_set
初始化一个vector
数组并返回。
2.2 排序+双指针
那么,我们是否可以直接比较数组进行查找交集呢,我们尝试一下。一般而言,经过排序的数组是更加容易进行比较的,所以我们首先对数组进行排序,这时数组就有了规律,我们总是可以很容易从头到尾的查找两个数组的相同元素。具体就是两个数组nums1
和nums2
分别使用一个工作指针i和j,当二者指向的元素相同时就将其加入结果vector数组
,否则就对指向较小元素的指针进行移动。
上述流程描述起来十分简单,但是在实现时我们需要注意数组元素是可能重复的,但是输出结果要求是不重复的,所以我们要对遍历到的数组进行去重。具体的去重方法分为以下两种:
- 在对数组
nums1
和nums2
的指针移动中进行去重:这个就是每次在判断两个指针指向的值相等时需要对指针进行同时迁移,所以为了避免重复就要判断i和j指针在迁移后指向的元素是否和前一个元素相等,若相等则继续进行移动。代码见3.2。可以与前面的第15题——三数之和作类比。 - 在进行
vector
结果数组的值插入时进行判断去重:就是在每次得到一个可更新进vector
的数值时判断这个数值是否已经被插入在尾部决定是否将其更新。
3.题目解答
3.1 哈希集合解法
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//保存返回结果的set
unordered_set<int> result_set;
//保存中间结果的set
unordered_set<int> uSet(nums1.begin(),nums1.end());
for(auto s2:nums2){
if(uSet.find(s2)!=uSet.end()){
result_set.insert(s2);
}
}
//使用set初始化结果vector
vector<int>result(result_set.begin(),result_set.end());
return result;
}
};
3.2 排序+双指针解法
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
//分别对两个数组进行排序
sort(nums1.begin(),nums1.end());
sort(nums2.begin(),nums2.end());
//初始化结果向量数组
vector<int> result;
//初始化工作指针
int i=0,j=0;
int len1 = nums1.size(),len2 = nums2.size();
//指针工作比较二者指向值是否相等
while(i<len1 && j<len2){
int num1 = nums1[i],num2= nums2[j];
if(num1<num2){
i++;
}else if(num1>num2){
j++;
}else{
//这里要注意数组越界的情况
result.push_back(num1);
i++;
j++;
//注意这里对数组nums1和nums2进行去重,可以与后面的方法比较
while(i<len1 && nums1[i]==nums1[i-1]){
i++;
}
while(j<len2 && nums2[j]==nums2[j-1]){
j++;
}
}
}
return result;
}
};
3.3 官方双指针代码——更简洁
class Solution {
public:
vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
int length1 = nums1.size(), length2 = nums2.size();
int index1 = 0, index2 = 0;
vector<int> intersection;
while (index1 < length1 && index2 < length2) {
int num1 = nums1[index1], num2 = nums2[index2];
if (num1 == num2) {
// 保证加入元素的唯一性
//注意这里直接在vector结果数组端进行去重
if (!intersection.size() || num1 != intersection.back()) {
intersection.push_back(num1);
}
index1++;
index2++;
} else if (num1 < num2) {
index1++;
} else {
index2++;
}
}
return intersection;
}
};
总结:两个数组相关的与双指针的联系更密切,unordered_set的初始化会对元素自动去重