LeetCode第350题_两个数组的交集II
LeetCode 第350题:两个数组的交集 II
📖 文章摘要
本文详细解析LeetCode第350题"两个数组的交集 II",这是一道哈希表应用的进阶问题。文章提供了三种解法:哈希表法、双指针法和二分查找法,包含C#、Python、C++三种语言实现,配有详细的思路分析和性能对比。适合想要深入理解哈希表和双指针技巧的程序员。
核心知识点: 哈希表、双指针、二分查找
难度等级: 简单
推荐人群: 数据结构进阶学习者、算法面试备考者
题目描述
给你两个整数数组 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,查找在哈希表中存在且次数大于0的元素
- 将找到的元素加入结果,并减少哈希表中对应数字的次数
-
双指针法
- 先对两个数组进行排序
- 使用两个指针分别遍历两个数组
- 比较两个指针指向的元素,相等则加入结果
-
二分查找法
- 对两个数组排序
- 遍历较短的数组,在较长的数组中二分查找对应元素
- 使用lowerBound记录二分查找的起始位置,优化查找效率
图解思路
哈希表法流程分析
步骤 | 操作 | 数据状态 | 说明 |
---|---|---|---|
初始状态 | - | nums1=[1,2,2,1], nums2=[2,2] | 原始输入 |
第一步 | 创建哈希表 | map={1:2, 2:2} | 记录nums1中元素出现次数 |
第二步 | 遍历nums2 | result=[2,2] | 找到交集元素 |
双指针法状态分析
情况 | nums1指针 | nums2指针 | 结果数组 | 说明 |
---|---|---|---|---|
初始状态 | [1,1,2,2] | [2,2] | [] | 排序后的初始状态 |
第一步 | 1 < 2 | - | [] | nums1指针后移 |
第二步 | 2 == 2 | - | [2] | 添加相等元素 |
第三步 | 2 == 2 | - | [2,2] | 添加相等元素 |
代码实现
C# 实现
public class Solution {public int[] Intersect(int[] nums1, int[] nums2) {if (nums1.Length > nums2.Length) {return Intersect(nums2, nums1);}Dictionary<int, int> count = new Dictionary<int, int>();List<int> result = new List<int>();foreach (int num in nums1) {if (!count.ContainsKey(num)) {count[num] = 0;}count[num]++;}foreach (int num in nums2) {if (count.ContainsKey(num) && count[num] > 0) {result.Add(num);count[num]--;}}return result.ToArray();}
}
Python 实现
class Solution:def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:if len(nums1) > len(nums2):return self.intersect(nums2, nums1)count = collections.Counter(nums1)result = []for num in nums2:if count[num] > 0:result.append(num)count[num] -= 1return result
C++ 实现
class Solution {
public:vector<int> intersect(vector<int>& nums1, vector<int>& nums2) {if (nums1.size() > nums2.size()) {return intersect(nums2, nums1);}unordered_map<int, int> count;vector<int> result;for (const int num : nums1) {++count[num];}for (const int num : nums2) {if (auto it = count.find(num); it != count.end() && it->second > 0) {result.push_back(num);--it->second;}}return result;}
};
执行结果
C# 实现
- 执行用时:132 ms
- 内存消耗:42.8 MB
Python 实现
- 执行用时:40 ms
- 内存消耗:15.2 MB
C++ 实现
- 执行用时:4 ms
- 内存消耗:10.3 MB
性能对比
解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
哈希表法 | O(m+n) | O(min(m,n)) | 实现简单,适用于无序数组 | 需要额外空间 |
双指针法 | O(mlogm + nlogn) | O(1) | 空间复杂度低 | 需要排序,修改输入 |
二分查找法 | O(min(mlogn, nlogm)) | O(1) | 适用于有序数组 | 需要排序,实现复杂 |
代码亮点
- 🎯 优化空间使用,总是哈希较短的数组
- 💡 使用Counter简化Python实现
- 🔍 C++中使用结构化绑定优化代码
- 🎨 保持代码结构清晰,易于维护
常见错误分析
- 🚫 忘记处理重复元素的次数
- 🚫 未考虑数组长度为0的情况
- 🚫 错误使用Set导致丢失重复元素
- 🚫 排序后未正确处理相等元素
解法对比
解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
---|---|---|---|---|
哈希表法 | O(m+n) | O(min(m,n)) | 无需排序,实现简单 | 需要额外空间 |
双指针法 | O(mlogm + nlogn) | O(1) | 空间利用率高 | 需要排序 |
二分查找法 | O(min(mlogn, nlogm)) | O(1) | 适合数据量差异大 | 实现复杂 |
相关题目
- LeetCode 349. 两个数组的交集 - 简单
- LeetCode 1002. 查找共用字符 - 简单
进阶思考
-
如果给定的数组已经排好序呢?你将如何优化你的算法?
- 可以使用双指针法,避免排序开销
- 时间复杂度降为O(m+n)
-
如果 nums1 的大小比 nums2 小很多,哪种方法更优?
- 可以使用二分查找法
- 时间复杂度为O(mlogn),其中m为较小数组长度
-
如果 nums2 的元素存储在磁盘上,内存是有限的,并且你不能一次加载所有的元素到内存中?
- 可以使用外部排序
- 分块加载nums2,每次与nums1的哈希表比较
📖 系列导航
🔥 算法专题合集 - 查看完整合集
📢 关注合集更新:点击上方合集链接,关注获取最新题解!目前已更新至第350题。
💬 互动交流
感谢大家耐心阅读到这里!希望这篇题解能够帮助你更好地理解和掌握这道算法题。
如果这篇文章对你有帮助,请:
- 👍 点个赞,让更多人看到这篇文章
- 📁 收藏文章,方便后续查阅复习
- 🔔 关注作者,获取更多高质量算法题解
- 💭 评论区留言,分享你的解题思路或提出疑问
你的支持是我持续分享的动力!
💡 一起进步:算法学习路上不孤单,欢迎一起交流学习!