LeetCode 探索初级算法-数组:06 两个数组的交集 II-20200318

06 两个数组的交集 II-20200318

题目

给定两个数组,编写一个函数来计算它们的交集。

示例

输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]

说明

  • 输出结果中每个元素出现的次数,应与元素在两个数组中出现的次数一致。
  • 我们可以不考虑输出结果的顺序。

进阶

  • 如果给定的数组已经排好序呢?你将如何优化你的算法?
  • 如果 nums1 的大小比 nums2 小很多,哪种方法更优?
  • 如果 nums2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?

注意事项

  1. 注意数组长度,尤其是空数组。
  2. 就算是重复的元素,也要算入交集中,就想示例中一样,就算有两个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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值