题干
设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构。
insert(val):当元素 val 不存在时,向集合中插入该项。
remove(val):元素 val 存在时,从集合中移除该项。
getRandom:随机返回现有集合中的一项。每个元素应该有相同的概率被返回。
示例 :
// 初始化一个空的集合。
RandomizedSet randomSet = new RandomizedSet();
// 向集合中插入 1 。返回 true 表示 1 被成功地插入。
randomSet.insert(1);
// 返回 false ,表示集合中不存在 2 。
randomSet.remove(2);
// 向集合中插入 2 。返回 true 。集合现在包含 [1,2] 。
randomSet.insert(2);
// getRandom 应随机返回 1 或 2 。
randomSet.getRandom();
// 从集合中移除 1 ,返回 true 。集合现在包含 [2] 。
randomSet.remove(1);
// 2 已在集合中,所以返回 false 。
randomSet.insert(2);
// 由于 2 是集合中唯一的数字,getRandom 总是返回 2 。
randomSet.getRandom();
分析
这道题就是想让我们设计一个数据结构,
要求能够以 **O(1)**查找,存储数据。
其实就会反映出来,这分别是链表和数组的特点。
好其实就该轮到哈希表了,关于哈希表都应该比较熟悉,相关数据结构的知识可以参考:
https://blog.csdn.net/woshimaxiao1/article/details/83661464
这位大佬写的很清楚了。
这里我们来复习一下Java的hashmap的常用函数操作:
$ hashmap.put(a,b):存放键值
$ hashmap.get(a)// 通过键对象查找得到值对象
$ hashmap .size()//大小
$ hashmap.isEmpty();
$ hashmap.containsKey(a)//是否含有键对象
$ hashmap.containsValue//是否含有值对象
说一下编码思路
一个List 来存储这些要存的数,每存一个数都存在List的末尾
一个HashMap来存储这些数和它对应的位置
插入:
先去HashMap查找是否有这个值,已经有就返回false;
如果没有,插入到list的末尾,在HashMap存储对应位置
删除
如果HashMap里没有这个数,返回false;
如果有这个数,先去HashMap查找对应的位置,
将list的最后一个数和要删除的这个数调换位置,然后删掉最后一个数。
这么做是因为:要求0(1),不能遍历,那就都删最后一个位置。把原来最后位置的那个数取出来,和要删除的数更换位置就行。
随机get
使用random的nextInt
java代码
class RandomizedSet {
List<Integer> sub;
HashMap<Integer,Integer> hm;
Random rand;
/** Initialize your data structure here. */
public RandomizedSet() {
sub =new ArrayList<Integer>();
hm=new HashMap<Integer,Integer>();
rand =new Random();
}
/** Inserts a value to the set. Returns true if the set did not already contain the specified element. */
public boolean insert(int val) {
if(hm.containsKey(val)){
return false;
}
else{
sub.add(val);
hm.put(val,sub.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(!hm.containsKey(val)){
return false;
}
else{//换位置,再删
int val_index=hm.get(val);
if(val_index<sub.size()-1){
int lastElement=sub.get(sub.size()-1);
sub.set(val_index,lastElement);
hm.put(lastElement,val_index);
}
hm.remove(val);
sub.remove(sub.size()-1);
return true;
}
}
/** Get a random element from the set. */
public int getRandom() {
return sub.get(rand.nextInt(sub.size()));
}
}
/**
* 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();
*/
速度很快,战胜99%+;
大佬代码:
class RandomizedSet {
Map<Integer,Integer> map;
List<Integer> list;
/** Initialize your data structure here. */
public RandomizedSet() {
map = new HashMap<Integer,Integer>();
list = new ArrayList<Integer>();
}
/** 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)){
map.put(val,list.size());
list.add(val);
return true;
}
return false;
}
/** Removes a value from the set. Returns true if the set contained the specified element. */
public boolean remove(int val) {
if(map.containsKey(val)){
int index = map.remove(val);
list.set(index,Integer.MIN_VALUE);
return true;
}
return false;
}
/** Get a random element from the set. */
public int getRandom() {
Random rm = new Random();
int i = rm.nextInt(list.size());
while(list.get(i) == Integer.MIN_VALUE){
i = rm.nextInt(list.size());
}
return list.get(i);
}
}
/**
* 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();
*/
总结
这道题很好玩
这道题是考察对已经掌握的数据结构的特点的掌握程度。
如果所有数据结构都重新定义(当然也可以)工程量十分巨大,建立在已有数据结构上定义一种新的其实就ok
(哈希表本身也是这样封装的