Swift - LeetCode - 两个数组的交集 II

724 阅读3分钟

我正在参加「掘金·启航计划」

题目

给你两个整数数组 nums1nums2,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

示例 1:

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

示例 2:

  • 输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
  • 输出: [4,9]

方法一:哈希表

思路及解法

由于同一个数字在两个数组中都可能出现多次,因此需要用哈希表存储每个数字出现的次数。对于一个数字,其在交集中出现的次数等于该数字在两个数组中出现次数的最小值。

首先遍历第一个数组,并在哈希表中记录第一个数组中的每个数字以及对应出现的次数,然后遍历第二个数组,对于第二个数组中的每个数字,如果在哈希表中存在这个数字,则将该数字添加到答案,并减少哈希表中该数字出现的次数。

为了降低空间复杂度,首先遍历较短的数组并在哈希表中记录每个数字以及对应出现的次数,然后遍历较长的数组得到交集。

代码

class Solution {
    func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
        if nums1.count > nums2.count {
            return intersect(nums2, nums1)
        }

        var map: [Int : Int] = [:]
        for num in nums1 {
            let count: Int = (map[num] ?? 0) + 1
            map[num] = count
        }

        var intersection: [Int] = []
        for num in nums2 {
            let count: Int = map[num] ?? 0
            if count > 0 {
                intersection.append(num)
                map[num] = count - 1
            }
        }
        return intersection
    }
}

复杂度分析

  • 时间复杂度:O(m+n)O(m+n),其中 mmnn 分别是两个数组的长度。需要遍历两个数组并对哈希表进行操作,哈希表操作的时间复杂度是 O(1)O(1),因此总时间复杂度与两个数组的长度和呈线性关系。

  • 空间复杂度:O(min(m,n))O(min(m,n)),其中 mmnn 分别是两个数组的长度。对较短的数组进行哈希表的操作,哈希表的大小不会超过较短的数组的长度。为返回值创建一个数组 intersectionintersection,其长度为较短的数组的长度。

方法二:排序 + 双指针

思路及解法

如果两个数组是有序的,则可以使用双指针的方法得到两个数组的交集。

首先对两个数组进行排序,然后使用两个指针遍历两个数组。

初始时,两个指针分别指向两个数组的头部。每次比较两个指针指向的两个数组中的数字,如果两个数字不相等,则将指向较小数字的指针右移一位,如果两个数字相等,将该数字添加到答案,并将两个指针都右移一位。当至少有一个指针超出数组范围时,遍历结束。

代码

class Solution {
    func intersect(_ nums1: [Int], _ nums2: [Int]) -> [Int] {
        let newNums1: [Int] = nums1.sorted()
        let newNums2: [Int] = nums2.sorted()
        let length1: Int = newNums1.count
        let length2: Int = newNums2.count
        var intersection: [Int] = []
        var index1 = 0
        var index2 = 0
        while index1 < length1 && index2 < length2 {
            if newNums1[index1] < newNums2[index2] {
                index1 += 1
            } else if newNums1[index1] > newNums2[index2] {
                index2 += 1
            } else {
                intersection.append(newNums1[index1])
                index1 += 1
                index2 += 1
            }
        }
        return intersection
    }
}

复杂度分析

  • 时间复杂度:O(mlogm+nlogn)O(m \log m+n \log n),其中 mmnn 分别是两个数组的长度。对两个数组进行排序的时间复杂度是 O(mlogm+nlogn)O(m \log m+n \log n),遍历两个数组的时间复杂度是 O(m+n)O(m+n),因此总时间复杂度是 O(mlogm+nlogn)O(m \log m+n \log n)

  • 空间复杂度:O(min(m,n))O(\min(m,n)),其中 mmnn 分别是两个数组的长度。为返回值创建一个数组 intersection,其长度为较短的数组的长度。不过在 C++ 中,我们可以直接创建一个 vector,不需要把答案临时存放在一个额外的数组中,所以这种实现的空间复杂度为 O(1)O(1)

结语

如果 nums2\textit{nums}_2 的元素存储在磁盘上,磁盘内存是有限的,并且你不能一次加载所有的元素到内存中。那么就无法高效地对 nums2\textit{nums}_2 进行排序,因此推荐使用方法一而不是方法二。在方法一中,nums2\textit{nums}_2 只关系到查询操作,因此每次读取 nums2\textit{nums}_2 中的一部分数据,并进行处理即可。