题目
设计一个数据结构,使如下3个操作的时间复杂度都是O(1)。
- insert(value):如果数据集中不包含一个数值,则把它添加到数据集中。
- remove(value):如果数据集中包含一个数值,则把它删除。
- getRandom():随机返回数据集中的一个数值,要求数据集中每个数字被返回的概率都相同。
分析
由于题目要求插入和删除(包括判断数据集中是否包含一个数值)的时间复杂度都是O(1),能够同时满足这些时间效率要求的只有哈希表,因此这个数据结构要用到哈希表。但是如果只用哈希表,则不能等概率地返回其中的每个数值。
如果数值是保存在数组中的,那么很容易实现等概率返回数组中的每个数值。假设数组的长度是n,那么等概率随机生成从0到n-1的一个数字。如果生成的随机数是i,则返回数组中下标为i的数值。由此可以发现,需要结合哈希表和数组的特性来设计这个数据容器。
由于数值保存在数组中,因此需要知道每个数值在数组中的位置,否则在删除的时候就必须顺序扫描整个数组才能找到待删除的数值,那就需要O(n)的时间。通常把每个数值在数组中的位置信息保存到一个HashMap中,HashMap的键是数值,而对应的值为它在数组中的位置。
解
java
public class Test {
public static void main(String[] args) {
RandomizedSet randomizedSet = new RandomizedSet();
randomizedSet.insert(1);
randomizedSet.insert(2);
randomizedSet.insert(3);
randomizedSet.insert(4);
for (int i = 0; i < randomizedSet.nums.size(); i++) {
System.out.println(randomizedSet.nums.get(i));
}
System.out.println("-----------------------");
randomizedSet.remove(2);
for (int i = 0; i < randomizedSet.nums.size(); i++) {
System.out.println(randomizedSet.nums.get(i));
}
System.out.println("-----------------------");
System.out.println(randomizedSet.getRandom());
}
static class RandomizedSet {
HashMap<Integer, Integer> numToLocation;
ArrayList<Integer> nums;
public RandomizedSet() {
numToLocation = new HashMap<>();
nums = new ArrayList<>();
}
public boolean insert(int val) {
if (numToLocation.containsKey(val)) {
return false;
}
numToLocation.put(val, nums.size());
nums.add(val);
return true;
}
public boolean remove(int val) {
if (!numToLocation.containsKey(val)) {
return false;
}
int location = numToLocation.get(val);
numToLocation.put(nums.get(nums.size() - 1), location);
numToLocation.remove(val);
nums.set(location, nums.get(nums.size() - 1));
nums.remove(nums.size() - 1);
return true;
}
public int getRandom() {
Random random = new Random();
int r = random.nextInt(nums.size());
return nums.get(r);
}
}
}