349. 两个数组的交集
1.1题目描述
https://leetcode-cn.com/problems/intersection-of-two-arrays
给定两个数组,编写一个函数来计算它们的交集。
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
- 输出结果中的每个元素一定是唯一的。
- 我们可以不考虑输出结果的顺序。
1.2代码详解
1)使用set集合
class Solution:
def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
set1 = set(nums1)
set2 = set(nums2)
if len(set1) < len(set2):
return self.set_insert(set1,set2)
else:
return self.set_insert(set2,set1)
def set_insert(self, set1, set2):
return [x for x in set1 if x in set2]
- 时间复杂度:O(m+n),其中 n 和 m 是数组的长度。将 nums1 转换为集合需要 O(n)的时间,类似地,将 nums2 转换为集合需要 O(m) 的时间。而在平均情况下,集合的 in/contains 操作只需要 O(1)的时间。
- 空间复杂度:O(m+n),最坏的情况是数组中的所有元素都不同。
2)内置函数
class Solution:
def intersection(self, nums1, nums2):
"""
:type nums1: List[int]
:type nums2: List[int]
:rtype: List[int]
"""
set1 = set(nums1)
set2 = set(nums2)
return list(set2 & set1)
3)双指针+排序
其实就是把两个数组进行排序,然后用双指针进行滑动对比就可以了。
- 先将nums1 与nums2 排序,然后游走两个指针,情况都写出来了,没有用else
- 时间复杂度:O(nlogn)
4)二分法 +排序
把一个短的数组如nums1进行排序,然后nums2中的每一个元素在nums1中进行二分查找。
将nums2排序,然后查找nums1的元素,需要准备一个binarySearch的辅助方法,注意left <= right
350. 两个数组的交集 II
2.1题目描述
https://leetcode-cn.com/problems/intersection-of-two-arrays-ii/
给定两个数组,编写一个函数来计算它们的交集。
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。(不同之处!!!)
我们可以不考虑输出结果的顺序。
进阶:
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
2.2代码详解
1)法一:排序双指针(适用于进阶1)
如果给定的数组已经排好序呢?你将如何优化你的算法?
将两个数组进行排序,随后用双指针顺序查找相同的元素
- 时间复杂度O(max(nlogn, mlogm, n+m))
- 空间复杂度O(1) (n,m分别为两个数组的长度)
进阶问题一中已排序的数组,则只需O(n)的时间复杂度
class Solution:
def intersect(self, nums1, nums2):
nums1.sort()
nums2.sort()
res = []
left, right = 0, 0
while left < len(nums1) and right < len(nums2):
if nums1[left] < nums2[right]:
left += 1
elif nums1[left] == nums2[right]:
res.append(nums1[left])
left += 1
right += 1
else:
right += 1
return res
2)法二:哈希计数 将较小的数组哈希计数(适用于进阶问题2)
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
随后在另一个数组中根据哈希来寻找。 时间复杂度O(max(n, m)) 空间复杂度O(min(n, m))
dict.get(key, default=None)根据key查找value,如果指定键的值不存在时,返回该默认值
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
counts = {} # key:元素 ,value:出现频次
res = []
for num in nums1:
counts[num] = counts.get(num, 0) + 1 # 记录nums1 中每个数出现的频次
# 遍历nums2
for num in nums2:
# counts[num] > 0 表示nums2 中有nums1 相同的元素
if num in counts and counts[num] > 0:
res.append(num)
counts[num] -= 1 # 查找到对应后的元素失效
return res
错误案例:
原因:如测试用例中的 [5] ,字典中没有这个值
3)法三:通过归并外排将两个数组排序后再使用排序双指针查找
如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
对应进阶问题三,如果内存十分小,不足以将数组全部载入内存,那么必然也不能使用哈希这类费空间的算法,只能选用空间复杂度最小的算法,即解法一。
但是解法一中需要改造,一般说排序算法都是针对于内部排序,一旦涉及到跟磁盘打交道(外部排序),则需要特殊的考虑。归并排序是天然适合外部排序的算法,可以将分割后的子数组写到单个文件中,归并时将小文件合并为更大的文件。当两个数组均排序完成生成两个大文件后,即可使用双指针遍历两个文件,如此可以使空间复杂度最低。