381.O(1)时间插入、删除和获取随机元素 - 允许重复
内心独白:看到这个题目都傻眼了有木有,这个题目看着很短要求却不低。三大要求难度其实不大,对于我们来说,Map集合、Set集合你熟悉使用了吗?这道题重点在于这两个集合的使用。
我们来点点剖析这道题目。
-
在初始化之前,我们创建一个数据结构,首先得站在巨人的肩膀上,我们分析需求,题目要求我们可以插入、删除和获取随机元素,这里可以知道我们必须使用一个含有键值对的数据结构,因此我们想到使用Map。除此之外,remove方法显然是排除相同值的,这里我们使用set。
// 创建数据结构 Map<Integer, Set<Integer>> map; List<Integer> list;
-
有了初始数据结构之后,我们的数据结构还是空的,因此我们需要对数据结构进行初始化
public void RandomizedCollection() { map = new HashMap<Integer, Set<Integer>(); list = new ArrayList<Integer>; }
-
数据结构已经创建完毕啦,这个时候就需要使用map与list去做事情,第一个要完成的任务就是完成插值操作,根据题目要求,添加同一个val的时候,第一次添加val返回true,第二次添加同样的val值则返回false。记住返回false并不代表这次添加失败了。每一次insert操作都可以成功添加键值对,这个boolean返回值只是让使用者知道/了解这次添加的值是否重复。
public boolean insert(int val) { list.add(val); Set<Integer> set = map.getOrDefault(val, new HashSet<Integer>()); /* * 这里大家可能会迷惑为什么要使用上面的语法,这里解释以下getOrDefault的作用,当被添加的val键已经存在的时候,map会返回对应val的值,否则则创建一个默认的值。 因此set集合里面是什么值并不重要,重要的是要知道当val相同的时候,map会调用已经存在的set,这样子就起到了判断这个键值对是否已经存在的作用。 */ set.add(list.size - 1); map.put(val, set); return set.size() == 1; }
-
接下来我们要实现题目要求的第二个要求,删除已经存在的元素,而且时间复杂度为O(1),这个要求很简单,因为题目对数据存放的位置没有限制,数据只要存在即可,因此我们可以先定位这个数据的位置,然后将其与最后一个数据交换后删除,如果这个数据已经位于最后,直接删除即可,这样子即可在O(1)复杂度的情况下删除这个数据。数据删除成功返回true,否则返回false
public boolean remove(int val) { if ( !map.containsKey(val)){ // map.containsKey(val)如果val存在则为true // 前面加了!则表示,该键不存在时进入if语句 return false; } Iterator<Integer> it = map.get(val).iterator(); int i = it.next(); int lastNum = list.get(num.size - 1); list.set(i, lastNum); map.get(val).remove(i); map.get(lastNum).remove(list.size() - 1); if(i < list.sieze - 1){ map.get(lastNum).add(i); } if(map.get(val).size == 0){ map.remove(val); } list.remove(list.size() - 1); return true; }
-
最后我们要进行最后一个获取随机数的操作
public int getRandom() { return list.get((int)(Math.random()*list.size())); }
到这里我们圆满把题目给写完啦