Leetcode 380 | O(1) 时间插入、删除和获取随机元素
题目描述
实现RandomizedSet 类:
RandomizedSet() 初始化 RandomizedSet 对象
bool insert(int val) 当元素 val 不存在时,向集合中插入该项,并返回 true ;否则,返回 false 。
bool remove(int val) 当元素 val 存在时,从集合中移除该项,并返回 true ;否则,返回 false 。
int getRandom() 随机返回现有集合中的一项(测试用例保证调用此方法时集合中至少存在一个元素)。
每个元素应该有 相同的概率被返回。
你必须实现类的所有函数,并满足每个函数的 平均 时间复杂度为 O(1) 。
示例
输入
["RandomizedSet", "insert", "remove", "insert", "getRandom", "remove", "insert", "getRandom"]
[[], [1], [2], [2], [], [1], [2], []]
输出
[null, true, false, true, 2, true, false, 2]
解释
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1); // 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomizedSet.remove(2); // 返回 false ,表示集合中不存在 2 。
randomizedSet.insert(2); // 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomizedSet.getRandom(); // getRandom 应随机返回 1 或 2 。
randomizedSet.remove(1); // 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomizedSet.insert(2); // 2 已在集合中,所以返回 false 。
randomizedSet.getRandom(); // 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
思路分析
采用动态数组ArrayList存放数据元素,HashMap存放数据元素对应的索引
ArrayList用法
ArrayList 是 List 接口的主要实现类,本质上,ArrayList 是对象引用的一个”变长”数组;
List是Collection 子接口之一;
List中元素特点有:有序、可重复、集合中的每个元素都有其对应的顺序索引。
List集合类中定义了许多方法:
- void add(int idx, Object elem) // 在idx插入elem元素
- Object get(int idx) //获取元素
- int indexOf(Object obj) // 返回obj在集合中首次出现的位置
- int lastIndexOf(Object obj) // 返回obj在集合中最后一次出现的位置
- Object remove(int idx) // 移除idx位置的元素
- Object set(int idx, Object elem) //设置指定idx位置元素为 elem
List接口的实现类还包括LinkedList、Vector;
三个实现类使用情景:
- ArrayList: 默认情况使用;
- LinkedList:频繁的插入删除;
- Vector:尽量避免使用,速度比ArrayList慢,但是线程安全。
HashMap用法
HashMap 是 Map 接口使用频率最高的实现类;
Map与 Collection 并列存在。用于保存具有映射关系的数据:key-value;
Map 中的 key 和 value 都可以是任何引用类型的数据。但常用 String 类作为 Map的“键”。
Map 接口的常用实现类:HashMap、LinkedHashMap、TreeMap 和Properties。
Map 常用方法:
//添加、修改
- Object put(Object key,Object value) // 将指定 key-value 添加到(或修改)当前 map 对象中
- void putAll(Map m) // 将 m 中的所有 key-value 对存放到当前 map 中
//删除
- Object remove(Object key) // 移除指定 key 的 key-value 对,并返回 value
- void clear() // 清空当前 map 中的所有数据
//元素查询的操作
- Object get(Object key) //获取指定 key 对应的 value
- boolean containsKey(Object key) // 判断是否包含指定的 key
- boolean containsValue(Object value): // 判断是否包含指定的 value
- int size() //返回 map 中 key-value 对的个数
- boolean isEmpty() //判断当前 map 是否为空
- boolean equals(Object obj) //判断当前 map 和参数对象 obj 是否相等
//元视图操作的方法
- Set keySet() // 返回所有 key 构成的 Set 集合
- Collection values() // 返回所有 value 构成的 Collection 集合
- Set entrySet() // 返回所有 key-value 对构成的 Set 集合
代码实现
class RandomizedSet {
// 变长数组+哈希表
List<Integer> nums;
Map<Integer, Integer> indices;
Random rand;
public RandomizedSet() {
// 变长数组存放元素
nums = new ArrayList<Integer>();
// 哈希表存放每个元素和对应的下标
indices = new HashMap<Integer, Integer>();
rand = new Random();
}
//当元素 val 不存在时,向集合中插入该项,并返回 true
public boolean insert(int val) {
// 判断val是否存在
if(indices.containsKey(val)){
return false;
}
// 获得数组长度
int index = nums.size();
// 数组末尾插入val
nums.add(val);
// 更新HashMap键值索引
indices.put(val,index);
return true;
}
// 交换数组被删除元素和最后一个元素,然后删除最后一项元素
public boolean remove(int val) {
// 判断当前数组是否包含被删除元素
if(!indices.containsKey(val)){
return false;
}
//获取当前元素再数组中的索引位置
int index = indices.get(val);
//获取数组最后一项元素的值
int last = nums.get(nums.size() -1);
// 把变长数组的最后一个元素存到删掉的元素位置处,并更新哈希索引表
nums.set(index, last);
indices.put(last, index);
// 删除nums的最后一个元素
nums.remove(nums.size() - 1);
// 删除哈希表中的对应元素和索引
indices.remove(val);
return true;
}
public int getRandom() {
// 生成一个0~nums.size()-1 范围内的整数
int randomIndex = rand.nextInt(nums.size());
return nums.get(randomIndex);
}
}
/**
* Your RandomizedSet object will be instantiated and called as such:
* RandomizedSet obj = new RandomizedSet();
* boolean param_1 = obj.insert(val);
* boolean param_2 = obj.remove(val);
* int param_3 = obj.getRandom();
*/