LeetCode 哈希表 380. 常数时间插入、删除和获取随机元素(设计数据结构 List HashMap底层 时间复杂度)

比起之前那些问计数哈希表的题目,这道题好像更接近哈希表的底层机制。
java中hashmap的实现是通过List<Node>,即链表的list,如果链表过长则换为红黑树,如果容量不足(装填因子下)则扩充数组容量。解决冲突的方式是直接接在对应位置的链表上。
首先看看哈希表几个操作的时间复杂度:
HashMap的新增:
- 计算key的哈希值
 - 哈希值作为index,找到对应的数组位置
 - 如果数组位置为空,直接存入
 - 如果数组位置不为空,遍历该链表,插入末尾
 
这里考虑理想情况(无冲突),时间复杂度为O1
HashMap的删除,查询都是一样的理解,如果没得冲突,都是O1的复杂度。
如果冲突,可能出现On情况(链表),Ologn情况(红黑树)
返回随机元素,这个则不好实现,因为HashMap是无序的,又没得类似List那样的索引,很难返回一个random的值。
接着考虑的一个方式就是,能不能把HashMap的key以0,1。。。的方式存到一个数组中,用random得到一个随机的序号,然后在通过序号去找。
然而这里犯了一个错误。这样的方式其实无疑与把这个HashMap变成了一个LIst。当然插入是O1,但是删除则不好操作了。
第二个想法是Hashset,但问题其实也一样,这是一个无序的set,没办法搞random。这里的无序set指的是插入进去之后放到的位置就是hash算出来的位置,显然无法用随机的方式使得每一个元素返回的概率相同。
第三个想法则是List作为基础,再用HashMap来补缺陷。
LIst的操作复杂度:
- append,直接在尾端加,O1
 - pop,直接去掉尾端,O1
 - 特定位置插入/删除,都需要萝卜挪坑,On
 - 访问特定位置元素,索引直接访问,O1
 - 查,要跑一遍整个数组,On
 
所以Random很好做到,其余的需要用append和pop搞事
append需要去重,我们把索引和值分别存入HashMap作为辅助
这样要插入时,先用HashMap判断有无(O1),然后直接插尾端(O1)
删除稍麻烦一些,我们如果直接删除真正位置,则需要挪位置变为On
所以用HashMap找到位置后,将该位置和List末尾做交换,然后PoP,这样就是O1了。
class RandomizedSet {
  Map<Integer, Integer> dict;
  List<Integer> list;
  Random rand = new Random();
  /** Initialize your data structure here. */
  public RandomizedSet() {
    dict = 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 (dict.containsKey(val)) return false;
    dict.put(val, list.size());
    list.add(list.size(), val);
    return true;
  }
  /** Removes a value from the set. Returns true if the set contained the specified element. */
  public boolean remove(int val) {
    if (! dict.containsKey(val)) return false;
    // move the last element to the place idx of the element to delete
    int lastElement = list.get(list.size() - 1);
    int idx = dict.get(val);
    list.set(idx, lastElement);
    dict.put(lastElement, idx);
    // delete the last element
    list.remove(list.size() - 1);
    dict.remove(val);
    return true;
  }
  /** Get a random element from the set. */
  public int getRandom() {
    return list.get(rand.nextInt(list.size()));
  }
}
LeetCode 哈希表 380. 常数时间插入、删除和获取随机元素(设计数据结构 List HashMap底层 时间复杂度)的更多相关文章
- Java实现 LeetCode 380 常数时间插入、删除和获取随机元素
		
380. 常数时间插入.删除和获取随机元素 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插入该项. remove( ...
 - LeetCode 381. Insert Delete GetRandom O(1) - Duplicates allowed O(1) 时间插入、删除和获取随机元素 - 允许重复(C++/Java)
		
题目: Design a data structure that supports all following operations in averageO(1) time. Note: Duplic ...
 - LeetCode380 常数时间插入、删除和获取随机元素
		
LeetCode380 常数时间插入.删除和获取随机元素 题目要求 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插 ...
 - Java实现 LeetCode 381 O(1) 时间插入、删除和获取随机元素 - 允许重复
		
381. O(1) 时间插入.删除和获取随机元素 - 允许重复 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插 ...
 - 381. O(1) 时间插入、删除和获取随机元素 - 允许重复
		
381. O(1) 时间插入.删除和获取随机元素 - 允许重复 LeetCode_381 题目详情 题解分析 代码实现 package com.walegarrett.interview; impor ...
 - Leetcode 381. O(1) 时间插入、删除和获取随机元素 - 允许重复
		
1.题目描述 设计一个支持在平均 时间复杂度 O(1) 下, 执行以下操作的数据结构. 注意: 允许出现重复元素. insert(val):向集合中插入元素 val. remove(val):当 va ...
 - Leetcode 380. 常数时间插入、删除和获取随机元素
		
1.题目描述 设计一个支持在平均 时间复杂度 O(1) 下,执行以下操作的数据结构. insert(val):当元素 val 不存在时,向集合中插入该项. remove(val):元素 val 存在时 ...
 - LeetCode 380. Insert Delete GetRandom O(1) 常数时间插入、删除和获取随机元素(C++/Java)
		
题目: Design a data structure that supports all following operations in averageO(1) time. insert(val): ...
 - [Swift]LeetCode380. 常数时间插入、删除和获取随机元素 | Insert Delete GetRandom O(1)
		
Design a data structure that supports all following operations in averageO(1) time. insert(val): Ins ...
 
随机推荐
- Openshift 4.4 静态 IP 离线安装系列:准备离线资源
			
本系列文章描述了离线环境下以 UPI (User Provisioned Infrastructure) 模式安装 Openshift Container Platform (OCP) 4.4.5 的 ...
 - 【String注解驱动开发】如何按照条件向Spring容器中注册bean?这次我懂了!!
			
写在前面 当bean是单实例,并且没有设置懒加载时,Spring容器启动时,就会实例化bean,并将bean注册到IOC容器中,以后每次从IOC容器中获取bean时,直接返回IOC容器中的bean,不 ...
 - C# .net framework .net core 3.1 请求参数校验, DataAnnotations, 自定义参数校验
			
前言 在实际应用场景中我们常常要对接口的入参进行校验, 例如分页大小是否正确, 必填参数是否已经填写等等. 最简单的实现方式如下图, 这种在实际开发中代码过于冗余, 而且不灵活. 今天介绍一种统一参数 ...
 - 记录一次更改服务器名称导致mysql 不能正常登录、启动
			
由于客户要求更改服务器的名称,以便区分多台服务器:修改前mysql 能正常登录,但是修改后,登录时报错: Enter password: ERROR 1524 (HY000): Plugin '*C6 ...
 - 懒羊羊找朋友(struct实现优先排序)
			
4907: 懒羊羊找朋友(点击) 时间限制: 1 Sec 内存限制: 128 MB ...
 - Java_图片转字符
			
把高达头像转换成字符[-V-] 调节双循环里y与x的增量改变字符输出的细节.高和长 public class ImgToStr { public static void main(String arg ...
 - WebDriverWait与expected_conditions结合使用
			
expected_conditions判断页面元素 demo2 from selenium import webdriver from selenium.webdriver.support.ui im ...
 - Python中的计时函数
			
我们已经知道使用cell magic或者line magic里面的%%time或者%time能够对Python程序中某一模块的运行时间进行计算,下面是一种更为灵活的计时方法,利用了计时函数time.t ...
 - 谁再悄咪咪的吃掉异常,我上去就是一 JIO
			
又到周末了,周更选手申请出站~ 这次分享一下上个月碰到的离奇的问题.一个简单的问题,硬是因为异常被悄咪咪吃掉,过关难度直线提升,导致小黑哥排查一个晚上. 这个美好的晚上,本想着开两把 LOL 无限火力 ...
 - Spring中的AOP(二)
			
2.5 Spring的织入 在上一篇文章中,我们介绍了Pointcut.Advice.Advisor三个必要模块,剩下的工作就是把它们拼装起来,也就是织入过程.在Spring中,使用类org.spri ...