LeetCode 第217题:存在重复元素
题目描述
给你一个整数数组 nums
。如果任何值在数组中出现至少两次,返回 true
;如果数组中每个元素互不相同,返回 false
。
难度
简单
题目链接
示例
示例 1:
输入:nums = [1,2,3,1]
输出:true
示例 2:
输入:nums = [1,2,3,4]
输出:false
示例 3:
输入:nums = [1,1,1,3,3,4,3,2,4,2]
输出:true
提示
1 <= nums.length <= 10^5
-10^9 <= nums[i] <= 10^9
解题思路
这是一个查找重复元素的问题,有多种解决方案,以下是三种常见的方法:
方法一:哈希表(Hash Table)
最直接的方法是使用哈希表(集合或字典)来跟踪已经出现过的元素:
- 创建一个哈希集合,用于存储已经遍历过的数字
- 遍历数组,对于每个元素,检查它是否已经在哈希集合中
- 如果元素已经在集合中,表示存在重复,返回
true
- 否则,将该元素添加到集合中,继续遍历
- 如果遍历完整个数组都没有发现重复元素,返回
false
时间复杂度:O(n),其中 n 是数组的长度
空间复杂度:O(n),最坏情况下需要存储所有元素
方法二:排序(Sorting)
另一种方法是先对数组进行排序,然后检查相邻元素是否相同:
- 对数组进行排序
- 遍历排序后的数组,检查相邻元素是否相同
- 如果找到相同的相邻元素,返回
true
- 如果遍历完整个数组都没有发现重复元素,返回
false
时间复杂度:O(n log n),排序的时间复杂度
空间复杂度:O(1) 或 O(n),取决于排序算法的实现
方法三:计数排序(Counting Sort)
如果知道数组中元素的范围不大,可以使用计数排序的思想:
- 创建一个布尔数组,大小为元素的范围
- 遍历原数组,标记已经出现过的元素
- 如果发现某个元素已经被标记过,返回
true
- 否则,标记该元素,继续遍历
- 如果遍历完整个数组都没有发现重复元素,返回
false
这种方法适用于元素范围较小的情况,如果元素范围很大,会导致空间复杂度过高,不适合使用。
代码实现
C# 实现
using System;
using System.Collections.Generic;
public class Solution {
// 方法一:哈希表
public bool ContainsDuplicate(int[] nums) {
HashSet<int> seen = new HashSet<int>();
foreach (int num in nums) {
if (seen.Contains(num)) {
return true;
}
seen.Add(num);
}
return false;
}
// 方法二:排序
public bool ContainsDuplicateSorting(int[] nums) {
Array.Sort(nums);
for (int i = 1; i < nums.Length; i++) {
if (nums[i] == nums[i - 1]) {
return true;
}
}
return false;
}
}
Python 实现
class Solution:
# 方法一:哈希表
def containsDuplicate(self, nums: List[int]) -> bool:
seen = set()
for num in nums:
if num in seen:
return True
seen.add(num)
return False
# 方法二:排序
def containsDuplicateSorting(self, nums: List[int]) -> bool:
nums.sort()
for i in range(1, len(nums)):
if nums[i] == nums[i - 1]:
return True
return False
# 方法三:使用Python的集合
def containsDuplicateSet(self, nums: List[int]) -> bool:
return len(nums) > len(set(nums))
C++ 实现
#include <vector>
#include <unordered_set>
#include <algorithm>
class Solution {
public:
// 方法一:哈希表
bool containsDuplicate(std::vector<int>& nums) {
std::unordered_set<int> seen;
for (int num : nums) {
if (seen.find(num) != seen.end()) {
return true;
}
seen.insert(num);
}
return false;
}
// 方法二:排序
bool containsDuplicateSorting(std::vector<int>& nums) {
std::sort(nums.begin(), nums.end());
for (int i = 1; i < nums.size(); i++) {
if (nums[i] == nums[i - 1]) {
return true;
}
}
return false;
}
};
性能分析
各语言实现的性能对比:
实现语言 | 方法 | 执行用时 | 内存消耗 | 说明 |
---|---|---|---|---|
C# | 哈希表 | 240 ms | 46.4 MB | 时间复杂度 O(n),空间复杂度 O(n) |
C# | 排序 | 252 ms | 46.2 MB | 时间复杂度 O(n log n),空间复杂度取决于排序实现 |
Python | 哈希表 | 468 ms | 26.1 MB | 时间复杂度 O(n),空间复杂度 O(n) |
Python | 排序 | 600 ms | 25.5 MB | 时间复杂度 O(n log n),空间复杂度 O(1) |
Python | 集合比较 | 444 ms | 26.0 MB | 时间复杂度 O(n),空间复杂度 O(n) |
C++ | 哈希表 | 88 ms | 51.4 MB | 时间复杂度 O(n),空间复杂度 O(n) |
C++ | 排序 | 76 ms | 46.6 MB | 时间复杂度 O(n log n),空间复杂度 O(1) |
补充说明
代码亮点
- 哈希表方法在实际应用中效率更高,尤其是对于大型数组
- Python 的集合比较方法(
len(nums) > len(set(nums))
)简洁优雅,一行代码解决问题 - C++ 的 unordered_set 实现提供了较好的性能平衡
优化方向
- 对于非常大的数组,可以考虑使用并行处理,将数组分成多个部分同时处理
- 如果元素范围已知且较小,可以使用位图(Bitmap)来减少空间消耗
- 对于几乎排序的数组,可以使用自适应排序算法提高效率
解题难点
- 选择合适的数据结构(哈希表、排序或位图等)
- 权衡时间和空间复杂度
- 处理大数据量时的性能问题
常见错误
- 只检查相邻元素是否相同(需要先排序)
- 使用 O(n²) 的双重循环进行比较,效率低下
- 在使用哈希表时忘记检查元素是否已存在就直接添加
- 在特定语言中选择不合适的集合类型(如使用 ArrayList 代替 HashSet)
相关题目
- 219. 存在重复元素 II - 判断是否存在距离不超过 k 的重复元素
- 220. 存在重复元素 III - 判断是否存在距离不超过 k 且差的绝对值不超过 t 的元素
- 287. 寻找重复数 - 在范围为 [1, n] 的数组中寻找唯一重复的数字
- 442. 数组中重复的数据 - 找出所有出现两次的元素