1 题目
题目:两数组的交集(Intersection of Two Arrays)
描述:给出两个数组,写出一个方法求出它们的交集。
- 结果中的每个元素必须是唯一的。
lintcode题号——547,难度——easy
样例1:
输入: nums1 = [1, 2, 2, 1], nums2 = [2, 2],
输出: [2].
样例2:
输入: nums1 = [1, 2], nums2 = [2],
输出: [2].
2 解决方案
2.1 思路
本题可以使用多种方式来解,排序+合并、排序+二分、哈希表。
使用先排序再合并的方式,在逐一合并的过程中找到交集,从两个数组头部开始,对比大小,将找到的相同值放入结果中,注意去除重复元素,以此类推直到其中一个数组为空即可。
使用二分搜索的方式,将其中一个数组排序,然后遍历另一个数组中的所有元素在已排序的数组中进行二分搜索,找到的元素放入结果中,同样需要去除重复元素。
使用哈希表的方式,将其中一个数组放入哈希表中,然后遍历另一个数组中的所有元素在哈希表中进行查找,找到的元素放入结果中,同样也要去除重复。
2.3 时间复杂度
假设数组一的长度m,数组二的长度n,m要大于n。
先排序再合并的方式,两个数组都排序耗时O(n*log n)
、O(m*log m)
,合并操作的耗时O(m + n)
,总时间复杂度为O(nlogn + mlogm + m + n) = O(n*log n + m*log m)
。
使用二分搜索的方式,对其中一个数组排序耗时O(m*log m)
,遍历另一个数组的耗时O(n)
,在已排序数组中进行二分搜索的耗时O(log m)
,总时间复杂度为O(m*log m + n*log m) = O((m+n)*log m)
,可以看出该方式中将较小的数组进行排序可以减少计算耗时。
使用哈希表的方式,将其中一个数组放入哈希表耗时O(m)
,遍历另一个数组的耗时O(n)
,在哈希表中查找一个元素的耗时为O(1)
,总时间复杂度为O(m + n)
。
2.4 空间复杂度
三种方式的空间复杂度也不一样。
先排序再合并的方式,不使用额外空间,空间复杂度为O(1)
。
使用二分搜索的方式,不使用额外空间,空间复杂度为O(1)
。
使用哈希表的方式,把较小的数组放入哈希表,使用的额外空间较小,空间复杂度为O(min{m,n})
。
3 源码
3.1 排序+合并的方式
细节:
- 排序+合并,时间复杂度O(nlog n + mlog m),空间复杂度O(1)。
C++版本:
/**
* @param nums1: an integer array
* @param nums2: an integer array
* @return: an integer array
* we will sort your return value in output
*/
vector<int> intersection(vector<int> &nums1, vector<int> &nums2) {
// write your code here
vector<int> result;
if (nums1.empty() || nums2.empty())
{
return result;
}
// 分别对两个数组排序
sort(nums1.begin(), nums1.end());
sort(nums2.begin(), nums2.end());
// 合并
int i = 0;
int j = 0;
while (i < nums1.size() && j < nums2.size())
{
if (nums1.at(i) < nums2.at(j))
{
i++;
}
else if (nums1.at(i) > nums2.at(j))
{
j++;
}
else
{
if (result.empty() || nums1.at(i) != result.back()) // 跳过重复的元素,先判断结果容器是否空,再取back(),否则越界
{
result.push_back(nums1.at(i));
}
i++;
j++;
}
}
return result;
}
3.2 二分搜索的方式
细节:
- 二分搜索,时间复杂度O((m+n)*log m),空间复杂度O(1)。
- 两个数组都可能包含重复元素,结果中使用set进行去重。
C++版本:
/**
* @param nums1: an integer array
* @param nums2: an integer array
* @return: an integer array
* we will sort your return value in output
*/
vector<int> intersection(vector<int> &nums1, vector<int> &nums2) {
// write your code here
vector<int> result;
if (nums1.empty() || nums2.empty())
{
return result;
}
// 比较两个数组长度
vector<int> * small = nullptr;
vector<int> * big = nullptr;
if (nums1.size() < nums2.size())
{
small = &nums1;
big = &nums2;
}
else
{
small = &nums2;
big = &nums1;
}
// 对较短的数组排序
sort(small->begin(), small->end());
// 遍历较长的数组元素,逐个进行二分搜索
unordered_set<int> resultSet;
for (auto it : *big)
{
// 二分搜索
if (binarySearch(*small, it))
{
resultSet.insert(it); // 去重
}
}
// 构造结果
for (auto it : resultSet)
{
result.push_back(it);
}
return result;
}
bool binarySearch(vector<int> & nums, int target)
{
int start = 0;
int end = nums.size() - 1;
while (start + 1 < end)
{
int mid = start + (end - start) / 2;
if (nums.at(mid) < target)
{
start = mid;
}
else if (nums.at(mid) > target)
{
end = mid;
}
else
{
return true;
}
}
if (nums.at(start) == target)
{
return true;
}
if (nums.at(end) == target)
{
return true;
}
return false;
}
3.3 哈希表的方式
细节:
- 哈希表,时间复杂度O(m + n),空间复杂度O(min{m,n})。
- 两个数组都可能包含重复元素,结果中使用set进行去重。
C++版本:
/**
* @param nums1: an integer array
* @param nums2: an integer array
* @return: an integer array
* we will sort your return value in output
*/
vector<int> intersection(vector<int> &nums1, vector<int> &nums2) {
// write your code here
vector<int> result;
if (nums1.empty() || nums2.empty())
{
return result;
}
// 比较两个集合大小
vector<int> * small;
vector<int> * big;
if (nums1.size() < nums2.size())
{
small = &nums1;
big = &nums2;
}
else
{
small = &nums2;
big = &nums1;
}
// 较小的放入hash表中
set<int> hash;
for (auto it : *small)
{
hash.insert(it);
}
// 遍历较大的集合
set<int> resultSet; // 产生的结果也需要去重,因为num2中可能有重复,不要求有序的话,可以使用unordered_set
for (auto it : *big)
{
if (hash.find(it) != hash.end())
{
resultSet.insert(it);
}
}
// 构造结果
for (auto it : resultSet)
{
result.push_back(it);
}
return result;
}