一题两解,顺便搞懂
HashSet的底层逻辑
一、题目描述(快速过)
给你一个整数数组 nums,
只要数组中存在任意重复元素,就返回 true,否则返回 false。
示例
输入:nums = [1,2,3,1]
输出:true
输入:nums = [1,2,3,4]
输出:false
二、解题思路总览
这道题本身不难,但它非常适合考察你对 Java 集合的理解深度。
我们用两种常见且实用的方法来解决:
- ✅ HashSet(推荐,最优解)
- ✅ 排序 + 相邻比较(不用额外集合)
三、方法一:HashSet(最推荐 ⭐)
核心思想
Set的特点:不允许重复元素- 如果往
Set里添加一个已经存在的元素,会失败
Java 实现代码
class Solution {
public boolean containsDuplicate(int[] nums) {
Set<Integer> set = new HashSet<>();
for (int x : nums) {
if (!set.add(x)) {
return true;
}
}
return false;
}
}
关键点拆解(非常重要)
1️⃣ set.add(x) 返回值含义
set.add(x)
- 如果
x之前不存在 → 返回true - 如果
x已经存在 → 返回false
所以:
if (!set.add(x)) {
return true;
}
等价于:
“如果当前元素已经出现过,直接返回 true”
时间 & 空间复杂度
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |
为什么 HashSet 这么快?
- 底层是 哈希表
- 插入 / 查询平均时间复杂度都是 O(1)
📌 面试官常问:
为什么不用 List?
👉 因为 List 的contains是 O(n),整体会变成 O(n²)
四、方法二:排序 + 相邻比较
思路说明
如果数组是有序的:
- 重复元素一定会相邻出现
- 只需要遍历一遍,比较
nums[i]和nums[i-1]
Java 实现代码
class Solution {
public boolean containsDuplicate(int[] nums) {
Arrays.sort(nums);
for (int i = 1; i < nums.length; i++) {
if (nums[i] == nums[i - 1]) {
return true;
}
}
return false;
}
}
时间 & 空间复杂度
| 项目 | 复杂度 |
|---|---|
| 时间复杂度 | O(n log n) |
| 空间复杂度 | O(1)(忽略排序内部开销) |
这种方法适合什么时候?
- ❌ 不要求保留原数组顺序
- ❌ 对性能要求没那么极致
- ✅ 不想使用额外数据结构
五、两种方法对比总结
| 对比点 | HashSet | 排序 |
|---|---|---|
| 时间复杂度 | O(n) ⭐ | O(n log n) |
| 空间复杂度 | O(n) | O(1) |
| 是否修改原数组 | 否 | 是 |
| 代码直观性 | ⭐⭐⭐ | ⭐⭐ |
| 面试推荐度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
📌 结论一句话:
能用 HashSet 就用 HashSet,
排序法是“备用方案”。
六、这道题真正考察什么?
- 是否理解 Set 的不重复特性
- 是否知道
add()的返回值含义 - 是否能权衡 时间复杂度 vs 空间复杂度
- 是否写出 简洁而有表达力的代码