由于题目要求插入和删除的时间复杂度都是O(1),能同时满足这些时间效率要求的只有哈希表,由此要用到哈希表。题目还要求可以在 O(1) 时间内随机返回数据集中的数值,数组可以做到,所以还需要用到数组。
数组中存放数值,可以实现在O(1) 时间内随机返回数据集中的数值,同时,哈希表需要知道每个数值在数组中的位置,否则删除数组的数值时就要顺序扫描整个数组。所以哈希表中键是数值,而其对应的值是它在数组中的位置。
public class RandomizedSet {
private Map<Integer, Integer> valueToIndex;
private List<Integer> values;
private Random random;
// 确保每个 RandomizedSet 对象都有自己独立的成员变量实例,避免不同对象之间共享数据
public RandomizedSet() {
valueToIndex = new HashMap<>();
values = new ArrayList<>();
random = new Random();
}
public boolean insert(int val) {
if (valueToIndex.containsKey(val)) {
return false;
}
valueToIndex.put(val, values.size());
values.add(val);
return true;
}
public boolean remove(int val) {
if (!valueToIndex.containsKey(val)) {
return false;
}
int index = valueToIndex.get(val);
int lastElement = values.get(values.size() - 1);
// 将最后一个元素移动到要删除元素的位置
values.set(index, lastElement);
valueToIndex.put(lastElement, index);
// 删除最后一个元素
values.remove(values.size() - 1);
valueToIndex.remove(val);
return true;
}
public int getRandom() {
int randomIndex = random.nextInt(values.size());
return values.get(randomIndex);
}
}
在数组和HashMap尾部添加数据的操作都是O(1),所以没什么好说的,就是别忘了考虑数据若已经存在,则不能添加,返回false。
删除数值前也要考虑数据集中是否存在该数据,没有则返回false。从哈希表中用O(1)时间删除数据比较简单,之间调用remove方法即可,但从数值中用O(1)时间删除数值,不能简单的remove了,因为被删除的数值不一定位于数组的尾部,当数组中间的数值被删除之后,为了保证数组内存的连续性,被删除之后的数字都要向前移动,由此删除的时间复杂度是O(n)了。所以为了避免数组中删除数值时移动数据,可以用数值尾部的数据替换掉把被删除的数字,同时删除数组中最后的数字。这样时间复杂度就是O(1).
以相同概率随机返回数据可以用函数random.nextInt随机生成一个整数,将其当作下标即可。