题目
Design a data structure that supports all following operations in average O(1) time.
insert(val)
: Inserts an item val to the set if not already present.remove(val)
: Removes an item val from the set if present.getRandom
: Returns a random element from current set of elements. Each element must have the same probability of being returned.
Example:
// Init an empty set.
RandomizedSet randomSet = new RandomizedSet();
// Inserts 1 to the set. Returns true as 1 was inserted successfully.
randomSet.insert(1);
// Returns false as 2 does not exist in the set.
randomSet.remove(2);
// Inserts 2 to the set, returns true. Set now contains [1,2].
randomSet.insert(2);
// getRandom should return either 1 or 2 randomly.
randomSet.getRandom();
// Removes 1 from the set, returns true. Set now contains [2].
randomSet.remove(1);
// 2 was already in the set, so return false.
randomSet.insert(2);
// Since 2 is the only number in the set, getRandom always return 2.
randomSet.getRandom();
十分钟尝试
可以用HashSet吗?添加,删除可以,但是如何实现随机返回呢?想起来数组,数组的读取也是O(1)的,我们初始化一个随机值当作数组的索引就可以实现随机读取了。来,代码上:
class RandomizedSet {
private Set<Integer> set;
private List<Integer> list;
/** Initialize your data structure here. */
public RandomizedSet() {
set=new HashSet();
list=new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(set.contains(val)) return false;
else{
return set.add(val);
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(!set.contains(val)) return false;
else{
return set.remove(val);
}
}
/** Get a random element from the set. */
public int getRandom() {
//每次随机之前,清空list,不然上次的数据都存在,即使remove也没有用
list.clear();
for(Integer tmp:set){
list.add(tmp);
}
Random random=new Random();
int randIndex=random.nextInt(list.size());
return list.get(randIndex);
}
}
/**
* 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();
*/
Runtime: 379 ms, faster than 1.72% of Java online submissions forInsert Delete GetRandom O(1).
记得getRandom前,清空list,否则以前的数组仍然存在。但是这样不符合题目要求,因为getRandom的时候需要clear,很显然,clear方法不是O(1)的
再来。
class RandomizedSet {
private Set<Integer> set;
private List<Integer> list;
/** Initialize your data structure here. */
public RandomizedSet() {
set=new HashSet();
list=new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(set.contains(val)) return false;
else{
list.add(val);
return set.add(val);
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(!set.contains(val)) return false;
else{
list.remove(val);
return set.remove(val);
}
}
/** Get a random element from the set. */
public int getRandom() {
Random random=new Random();
int randIndex=random.nextInt(list.size());
return list.get(randIndex);
}
}
/**
* 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();
*/
remove后list还有数据,我判断错了,其实是remove按照索引处理了,不是值。修改后成功了。
list.remove有两个方法,如果传入的int,当索引处理,如果传入对象,比如Integer,按值处理。
/** * Removes a value from the set. Returns true if the set contained the specified element. */ public boolean remove(int val) { if (!set.contains(val)) return false; else { list.remove(Integer.valueOf(val)); return set.remove(val); } } 修改后写了下面一版本。成功了:
class RandomizedSet {
private Set<Integer> set;
private List<Integer> list;
/** Initialize your data structure here. */
public RandomizedSet() {
set=new HashSet();
list=new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if (set.contains(val)) return false;
else {
list.add(val);
return set.add(val);
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if (!set.contains(val)) return false;
else {
list.remove(Integer.valueOf(val));
return set.remove(val);
}
}
/** Get a random element from the set. */
public int getRandom() {
Random random=new Random();
int randIndex=random.nextInt(list.size());
return list.get(randIndex);
}
}
/**
* 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();
*/
虽然成功了,但是时间复杂度对吗?getRandom对,insert也对,remove呢?关键是remove中的list.remove(Object obj)是O(1)吗?如果是索引,需要移动数组,不是常量时间,如果是值,也不是。这个方法也不对。
- add() – takes O(1) time
- add(index, element) – in average runs in O(n) time
- get() – is always a constant time O(1) operation
- remove() – runs in linear O(n) time. We have to iterate the entire array to find the element qualifying for removal,如果删除的是最后一个元素,入参是索引应该是O(1)
- indexOf() – also runs in linear time. It iterates through the internal array and checking each element one by one. So the time complexity for this operation always requires O(n) time
- contains() – implementation is based on indexOf(). So it will also run in O(n) time
- set(index,element)时间复杂度是O(1)
写了下面一版,主要思路是利用list的常量时间复杂度操作,set(index,element)和remove(lastindex) 完成。今天状体也不太好,所以调试了好久才发现很多问题,先把有问题代码贴出来,一个一个分析:
1. 定义了一个index,insert完之后增加,用来记录新添加元素的索引。这个是严重错误的,因为删除后,index是一直增加的,明显不对,重大错误!
2. list的add(index,element)是增加元素到指定位置,后面的元素依次后移动,不是覆盖,覆盖指定位置元素,用set(index,elemment)
3. 看下面的两行代码
map.put(lastElement,index);
map.remove(val);
先更新map中的索引,然后再删除。顺序刚开始写反了,如果是一个元素,等于从map移除后,又添加进入了map
class RandomizedSet {
private Map<Integer,Integer> map;
private List<Integer> list;
private int index=0;
/** Initialize your data structure here. */
public RandomizedSet() {
map=new HashMap();
list=new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(map.containsKey(val)) return false;
else{
list.add(val);
map.put(val,index++);
return true;
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(!map.containsKey(val)) return false;
else{
//会产生不连续空间,因此记录索引,直接用最后一个元素覆盖
int index=map.get(val);
int lastElement=list.get(list.size()-1);
list.set(index,lastElement);
list.remove(list.size()-1);
map.put(lastElement,index);
map.remove(val);
return true;
}
}
/** Get a random element from the set. */
public int getRandom() {
Random random=new Random();
int randIndex=random.nextInt(list.size());
return list.get(randIndex);
}
}
/**
* 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();
*/
修改后代码如下:
效率有所提高
class RandomizedSet {
private Map<Integer,Integer> map;
private List<Integer> list;
/** Initialize your data structure here. */
public RandomizedSet() {
map=new HashMap();
list=new ArrayList();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(map.containsKey(val)) return false;
else{
list.add(val);
map.put(val,list.size()-1);
return true;
}
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(!map.containsKey(val)) return false;
else{
//会产生不连续空间,因此记录索引,直接用最后一个元素覆盖
int index=map.get(val);
int lastElement=list.get(list.size()-1);
list.set(index,lastElement);
list.remove(list.size()-1);
map.put(lastElement,index);
map.remove(val);
return true;
}
}
/** Get a random element from the set. */
public int getRandom() {
Random random=new Random();
int randIndex=random.nextInt(list.size());
return list.get(randIndex);
}
}
/**
* 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();
*/