06 两个数组的交集 II-20200318
题目
给定两个数组,编写一个函数来计算它们的交集。
示例
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
说明
- 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
- 我们可以不考虑输出结果的顺序。
进阶
- 如果给定的数组已经排好序呢?你将如何优化你的算法?
- 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
注意事项
- 注意数组长度,尤其是空数组。
- 就算是重复的元素,也要算入交集中,就想示例中一样,就算有两个2也要写出来。
思路一
先用set()操作筛选无重复的数组元素,然后看相同元素中,两个数组中是否出现里相同次数,最后把输出相同次数的重复元素。
修改经历:
1. 写得太复杂了,但是执行的时候函数出错了。先化简下。注意到,测试的答案中,并没有排序!(第一次提交)
- 解答错误
2. 将前面的set()操作删除,只剩下一个双层的循环,但是这次提交也是错误,在 [1,2] 和 [2,1] 输入后得到的是 [1] 而不是 [1,2]。主要是第二个数组pop()的index没有设置好。(第二次提交)
- 解答错误
3. 这次成功了,但是成绩惨不忍睹。看来双层循环不是好的答案啊。(提三次提交)
- 执行用时 :176 ms, 在所有 Python3 提交中击败了5.33%的用户
- 内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
有时候自己写的代码自己都发愁,这是啥玩意啊。发现自己很容易掉入一个细节内,浪费大量的时间,应该是先考虑整体,再回来看细节。
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
final_intersect = []
for i in range(len(nums1)-1, -1, -1):
for j in range(len(nums2)-1, -1, -1):
if nums1[i] == nums2[j]:
final_intersect.insert(0, nums1[i])
nums2.pop(j)
break
else:
pass
return final_intersect
思路二
参考大神的题解,发现和我之前想的hash做法一样,但是我之前都是直接把代码放上去的,没有自己写过,所以这次没写出来。因此之后的代码都要写,不管是谁的。
思路二的做法是针对无序的数组,采用字典将项的值看做键,将重复次数看做值。对数组分别遍历。
修改经历:
1. 在遍历第二个数组时,少一个判断条件。(第一次提交)
- 解答错误
2. 这一次成功了,用时也少了很多,但是内存依旧不理想。毕竟还是用到字典。(第二次提交)
- 执行用时 :64 ms, 在所有 Python3 提交中击败了52.98%的用户
- 内存消耗 :13.7 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
采用hash的算法,可以针对无序的数组。可以对较小的数组进行hash遍历。
- 时间复杂度:O(n + m)。其中 n,m 分别代表了数组的大小。
- 空间复杂度:O(min(n,m)),我们对较小的数组进行哈希映射使用的空间。
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
dict = {}
for item in nums1:
if item in dict:
dict[item] += 1
else:
dict[item] = 1
final = []
for item in nums2:
if item in dict and dict[item] > 0:
final.append(item)
dict[item] -= 1
else:
pass
return final
思路三
还可以,提前对数组排好顺序,再操作(也可以直接针对已经排好序的数组)。排好序好之后,两个指针同时操作。因为是排好序的,所以数组时递增的,两个比较,相等就存下来。若不相等,大的不动,小的加一。
修改经历:
1. 输出的结构没有算入重复项的重复次数。主要是判断条件用的是 or,这个应该用 and,只要一个不行就不行。(第一次提交)
- 解答错误
2. 这次没有问题,耗时由上升一步,而且我还是用了排序,如果像进阶中说的那样,排好序的数组,这个方法耗时还能再少一些。但是内存还是很大,可能有最后的输出数组吧。如果在数组一上直接删除,就不用那么多内存了。(第二次提交)
- 行用时 :52 ms, 在所有 Python3 提交中击败了81.65%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.40%的用户
3. 这次没有新建最后的输出数组,而是直接在第一个数组上操作,但是出现了当输入为[1],[0]时,输出错误的问题。也就是没有考虑空数组的情况。(第三次提交)
- 解答错误
4. 超出时间限制了?为什么呢,怎么可能呢?卧槽。。。else没对齐(第四次提交)
- 解答错误
5. 又错了,应该是我把第一个数组默认为最大数值小的那个数组了,而当情况相反时,就是数组的最大值大于数组一时,就出错了。做一个判断就好。(第五次提交)
- 执行用时 :68 ms, 在所有 Python3 提交中击败了49.74%的用户
- 内存消耗 :13.6 MB, 在所有 Python3 提交中击败了5.40%的用户
心得体会:
我就想知道这个内存怎么变小?这怎么和给出的内存复杂度不一样呢?
- 时间复杂度:O(nlogn+mlogm)。其中 n,m 分别代表了数组的大小。我们对数组进行了排序然后进行了线性扫描。
- 空间复杂度:O(1),我们忽略存储答案所使用的空间,因为它对算法本身并不重要
最终代码展示:
class Solution:
def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
nums1.sort()
nums2.sort()
i, j = 0, 0
if len(nums1) and len(nums2):
while i < len(nums1) and j < len(nums2):
if nums1[i] == nums2[j]:
i += 1
j += 1
elif nums1[i] > nums2[j]:
nums2.pop(j)
else:
nums1.pop(i)
if nums1 == nums2:
return nums1
elif len(nums1) > len(nums2):
return nums2
else:
return nums1
else:
return []
还有一种效果一般,但是写法很炫酷的写法,就不单独拿出写了。
class Solution: def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]: inter = set(nums1) & set(nums2) l = [] for i in inter: l += [i] * min(nums1.count(i), nums2.count(i)) return l