📝 LeetCode 380 O(1) 时间插入、删除和获取随机元素
🔍 题目链接
🎯 题目核心要求
insert(val):元素不存在则插入,返回 true;存在返回 false,要求 O(1)remove(val):元素存在则删除,返回 true;不存在返回 false,要求 O(1)getRandom():等概率随机返回集合内任意元素,要求 O(1)
💡 解题思路
单纯 HashMap 无法按下标随机取值;普通数组删除中间元素需要整体移位,耗时 O(n)。
数组 + HashMap 组合方案,实现三大操作全部 O(1) ✨
- 数组
nums:存放真实元素,依靠下标实现随机取值,保证抽取概率均等 - HashMap
map:key=存入的数值,value=数值在数组中的下标,快速判重、快速定位位置 - 🧩 删除核心优化技巧:
- 获取待删元素下标
loc、数组最后有效下标idx - 若
loc != idx:把数组末尾元素覆盖到 loc 位置,同步修改 map 里末尾元素对应的下标 - 有效下标
idx自减,逻辑丢弃原数组末尾(待删元素已被覆盖失效) - 全程无数组移位,删除操作 O(1)
- 获取待删元素下标
✅ 完整可直接提交代码
java
class RandomizedSet {
// 定长数组存放元素,题目数据范围不超过200000
static int[] nums = new int[200010];
Random random = new Random();
// key:存储的值,value:值在nums数组中的下标
Map<Integer, Integer> map;
// 当前数组最后一个有效元素下标,初始-1代表集合为空
int idx = -1;
public RandomizedSet() {
map = new HashMap<>();
}
public boolean insert(int val) {
// 已存在直接返回false
if(map.containsKey(val)) return false;
idx++;
nums[idx] = val;
map.put(val, idx);
return true;
}
public boolean remove(int val) {
// 不存在直接返回false
if(!map.containsKey(val)) return false;
// 取出待删除元素下标,并从map移除该key
int loc = map.remove(val);
// 如果待删元素不是数组最后一位,交换末尾元素到loc位置
if(loc != idx){
int lastVal = nums[idx];
nums[loc] = lastVal;
map.put(lastVal, loc);
}
// 有效下标前移一位,逻辑删除末尾数据
idx--;
return true;
}
public int getRandom() {
// 随机取 [0, idx] 下标,每个元素等概率抽取
return nums[random.nextInt(idx + 1)];
}
}
🔍 关键细节笔记
1. 📦 成员变量说明
nums:静态数组,提前分配足够空间,省去动态扩容开销;存储所有有效数字map:哈希映射,记录数值对应数组下标,O(1) 判断存在 + O(1) 定位下标idx有效尾下标:集合空时idx=-1,集合总元素数量 =idx + 1Random:生成均匀随机下标,保障getRandom每个元素抽取概率完全相等
2. ➕ insert 执行流程
- 哈希表判断 val 是否存在,重复直接返回 false
- 有效下标
idx自增 1 - 数组存入 val,哈希表记录 val 对应的新下标
3. 🗑️ remove 核心交换逻辑(重难点)
示例:数组 [1,2,3,4],idx=3,删除2(loc=1)
- map 删除 key=2,拿到位置 loc=1
- 末尾值 4 覆盖 nums1,数组变为
[1,4,3,4] - map 更新 key=4 的下标为 1
- idx 减为 2,有效数组仅保留前三位
[1,4,3]
优势:无需数组整体前移,删除操作稳定 O(1)
边界:如果待删元素刚好是数组最后一个元素(loc == idx),无需覆盖、更新map,直接 idx-- 即可
4. 🎲 getRandom 等概率原理
random.nextInt(n) 均匀返回 [0, n-1] 的随机整数
当前有效元素总数 total = idx + 1,调用 nextInt(idx+1) 得到 0 ~ idx 的随机下标,数组每个下标对应唯一元素,抽取概率完全均等
⏱️ 复杂度分析
- 时间复杂度:insert / remove / getRandom 均为 O(1)
- 空间复杂度:O(N),N 为集合内存储元素数量
⚠️ 高频易错点总结
- remove 中必须先执行
map.remove(val)获取 loc,再处理末尾元素交换 - 交换末尾元素后,一定要更新 map 中末尾值对应的下标,否则下标映射错乱
- getRandom 随机数范围是
idx+1,不能只写 idx,否则取不到最后一个有效元素 - 静态数组 nums 全局共用不影响多组用例:每个实例的 idx 独立,旧无效数据不会被访问到