LeetCode刷題Medium篇Insert Delete GetRandom O(1)
題目
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
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();
*/