@[TOC](【刷题之路】LeetCode 349(350). 两个数组的交集Ⅰ(Ⅱ))
一、题目描述
原题连接:
349. 两个数组的交集
350. 两个数组的交集 II
题目描述:
1、349. 两个数组的交集
给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [9,4]
解释:[4,9] 也是可通过的
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
2、350. 两个数组的交集 II
给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。
示例 1:
输入: nums1 = [1,2,2,1], nums2 = [2,2]
输出: [2,2]
示例 2:
输入: nums1 = [4,9,5], nums2 = [9,4,9,8,4]
输出: [4,9]
提示:
1 <= nums1.length, nums2.length <= 1000
0 <= nums1[i], nums2[i] <= 1000
进阶:
如果给定的数组已经排好序呢?你将如何优化你的算法?
如果 nums1 的大小比 nums2 小,哪种方法更优?
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中,你该怎么办?
二、解题
1、方法1——去重后遍历
1.1、思路分析
因为349题中的返回不允许有重复数元素,所以我们要先对两个数组进行去重操作。
对两个数组进行去重后再对两个数组进行双层遍历,遍历nums1的每个元素,对于每个元素,都在nums2中寻找,看看它在nums2中是否存在,若存在,则将其写入一个答案数组。
1.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
// 先写一个函数,比较两个整数的大小
int cmp_int(const void* p1, const void* p2) {
assert(p1 && p2);
return *((int*)p1) - *((int*)p2);
}
// 写一个函数,用双指针的思路对一个数组进行去重,并返回去重后数组的长度
int Deduplication(int* nums, int numsLen) {
assert(nums);
// 先对数组进行排序
qsort(nums, numsLen, sizeof(nums[0]), cmp_int);
int cur = 0;
int give = 0;
for (cur = 1; cur < numsLen; cur++) {
if (nums[cur] != nums[give]) {
nums[give + 1] = nums[cur];
give++;
}
}
return give + 1;
}
int* intersection1(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
assert(nums1 && nums2);
// 先对nums1和nums2进行去重
nums1Size = Deduplication(nums1, nums1Size);
nums2Size = Deduplication(nums2, nums2Size);
int* ans = (int*)malloc(nums1Size * sizeof(int));
if (NULL == ans) {
perror("intersection");
return NULL;
}
int target = 0;
int j = 0;
int n = 0;
for (target = 0; target < nums1Size; target++) {
for (j = 0; j < nums2Size; j++) {
if (nums2[j] == nums1[target]) {
ans[n] = nums1[target];
n++;
break;
}
}
}
*returnSize = n;
return ans;
}
时间复杂度:O(mn),m和n分别为两个数组的元素个数。对两个数组进行排序的时间复杂度分别为O(mlogm)和O(nlogn),去重的时间复杂度分别为O(m)和O(n)。最后循环的时间复杂度O(mn),故总的时间按复杂度为O(mn)。
空间复杂度:O(logm + logn),m和n分别是两个数组的元素个数,空间复杂度主要取决于对两个数组进行排序时所使用的额外空间。
因为双层遍历的方法不好统计交集中的元素在分别在两个数组中出现的次数,所以就不将此思路运用于350题中。
2、方法2——排序后双指针
2.1、思路分析
对两个数组进行升序排序之后,就可以使用双指针的方法求得两个数组的交集,具体步骤如下:
首先我们让两个指针p1和p2分别指向nums1和nums2的第一个元素;
如果p1和p2所指向的元素不相等,则应该让指向元素较小的指针向右移动一位,以找到可能与较大元素相等的元素:
我们需要用一个变量pre来记录前一个加入答案数组的元素:
如果p1和p2所指向的元素相等且不等于pre则将该元素写入答案数组,
并将pre更新为该元素,然后p1和p2都向右边移动一位。如果只是p1和p2所指向的元素相等但却等于pre,则只让p1和p2都向右移动一位。
只要有其中一个指针指向了数组的末尾,循环就可以停止。
2.2、代码实现
有了以上思路,那我们写起代码来也就水到渠成了:
int* intersection2(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
assert(nums1 && nums2);
// 先对两个数组进行排序
qsort(nums1, nums1Size, sizeof(nums1[0]), cmp_int);
qsort(nums2, nums2Size, sizeof(nums2[0]), cmp_int);
int min = nums1Size < nums2Size ? nums1Size : nums2Size;
int* ans = (int*)malloc(min * sizeof(nums1[0]));
if (NULL == ans) {
perror("intersection2");
return NULL;
}
int n = 0;
int pre = -1;
int p1 = 0;
int p2 = 0;
while ((p1 < nums1Size) && (p2 < nums2Size)) {
if (nums1[p1] < nums2[p2]) {
p1++;
}
else if (nums2[p2] < nums1[p1]) {
p2++;
}
else {
if (nums1[p1] != pre) {
ans[n] = nums1[p1];
n++;
pre = nums1[p1];
p1++;
p2++;
}
else {
p1++;
p2++;
}
}
}
*returnSize = n;
return ans;
}
时间复杂度:O(mlogm+ nlogn),m和n分别为两个数组的元素个数,对两个数组进行排序的时间复杂度分别是O(mlogm)和O(nlogn),双指针求交集的时间复杂度为O(m + n),故总时间复杂度为O(mlogm + nlogn)。
空间复杂度:O(logm + logn),m和n分别为两个数组的元素个数,空间复杂度主要取决于排序所使用的额外空间。
2.3、提升为350题解法
思路分析:
要将此方法提升为350题的解法其实只需要更改一点儿条件即可,就是:
当p1和p2所指向的元素相等时,就将其写入答案数组,并且p1和p2都向后移动一位,
当p1和p2所指向的元素不相等时,优先让指向元素较小的指针向后移动一位。
只要有其中一个指针指向了数组的末尾,循环就可以停止。
代码实现:
// 有了以上改进思路,那我们再写起代码来也就水到渠成了:
// 先写一个函数,比较两个整数的大小
int cmp_int(const void* p1, const void* p2) {
assert(p1 && p2);
return *((int*)p1) - *((int*)p2);
}
int* intersect(int* nums1, int nums1Size, int* nums2, int nums2Size, int* returnSize) {
assert(nums1 && nums2);
// 先对两个数组进行排序
qsort(nums1, nums1Size, sizeof(nums1[0]), cmp_int);
qsort(nums2, nums2Size, sizeof(nums2[0]), cmp_int);
int min = nums1Size < nums2Size ? nums1Size : nums2Size;
int* ans = (int*)malloc(min * sizeof(nums1[0]));
if (NULL == ans) {
perror("intersect");
return NULL;
}
int n = 0;
int p1 = 0;
int p2 = 0;
while ((p1 < nums1Size) && (p2 < nums2Size)) {
if (nums1[p1] == nums2[p2]) {
ans[n] = nums1[p1];
p1++;
p2++;
n++;
}
else {
if (nums1[p1] < nums2[p2]) {
p1++;
}
else {
p2++;
}
}
}
*returnSize = n;
return ans;
}
时间复杂度:O(mlogm + nlogn),m和n为两个数组的元素个数,对两个数进行排序的时间复杂度为O(mlogm+nlogn),
用双指针进行遍历的时间复杂度为O(m+n)故最终的时间爱你复杂度为O(mlogm+nlogn)
空间复杂度:O(logm + logn),m和n分别为两个数组的元素个数,空间复杂度主要取决于排序所使用的额外空间。