【刷题之路】LeetCode 349(350). 两个数组的交集Ⅰ(Ⅱ)

文章介绍了两种解决LeetCode349和350题目的方法,即找到两个数组的交集。方法一是先去重再遍历,方法二是排序后使用双指针。对于350题,需要考虑元素出现的次数。两种方法的时间复杂度和空间复杂度也进行了分析。
摘要由CSDN通过智能技术生成

@[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分别为两个数组的元素个数,空间复杂度主要取决于排序所使用的额外空间。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林先生-1

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值