【Leetcode】705- 设计哈希集合

devtools/2024/10/18 19:28:21/

问题描述

不使用任何内建的哈希表库设计一个哈希集合(HashSet)。

实现 MyHashSet 类:
void add(key) 向哈希集合中插入值 key 。
bool contains(key) 返回哈希集合中是否存在这个值 key 。
void remove(key) 将给定值 key 从哈希集合中删除。如果哈希集合中没有这个值,什么也不做。

示例:
输入:
[“MyHashSet”, “add”, “add”, “contains”, “contains”, “add”, “contains”, “remove”, “contains”]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
输出:
[null, null, null, true, false, null, true, null, false]
解释:
MyHashSet myHashSet = new MyHashSet();
myHashSet.add(1); // set = [1]
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(1); // 返回 True
myHashSet.contains(3); // 返回 False ,(未找到)
myHashSet.add(2); // set = [1, 2]
myHashSet.contains(2); // 返回 True
myHashSet.remove(2); // set = [1]
myHashSet.contains(2); // 返回 False ,(已移除)

提示:
0 < = k e y < = 1 0 6 0 <= key <= 10^6 0<=key<=106
最多调用 1 0 4 10^4 104 次 add、remove 和 contains

思路分析

哈希集合HashSet 是指能以 O(1) 时间进行插入和删除,可以保存不重复元素的一种数据结构。(需要区别于HashMap,以 key : value 形式存储数据。Python中HashMap即为dict、collections.OrderedDict,可详见该篇底层实现原理分析)

本问题中由于key都是int类型,因此我们可以直接采用一个数组存放,即HashSet[key]=key。这样插入、删除和查找元素的时间复杂度都为O(1)。但空间复杂度为O(n),当数据量特别大时,空间复杂度很高。我们需要设计一个更好的算法

回顾数据结构知识。设计一个哈希集合,一般需要考虑这几个条件:

  • 散列函数。将待存储的key映射到散列表中。常用的hash函数是取模运算,为了减少冲突,模一般为质数。
  • 冲突处理。当不同的key被映射到散列表的同一位置,就需要处理冲突。常用的是链地址法,散列表的元素不直接存储元素而是会指向一个链表表头,链表中保存key及后续冲突的key。
  • 可扩展性。当当前散列表已经不能满足继续存储元素时(例如每个位置都满了、冲突也已经很多),需要能够对散列表扩容。

我们依照实现即可。
(1)散列函数。数据规模最大为 1 0 6 10^6 106,我们考虑建一个长度为 1 0 4 10^4 104的散列表, 1 0 2 10^2 102为容许的冲突个数。根据质数表,取一个接近的质数为10009。
(2)冲突处理。就使用最经典的链地址法。散列函数映射结果相同的key,存储在散列表同一位置指向的链表(或数组)中,追加在链表(或数组)末尾。
(3)散列表使用固定大小的数组,提前确定了长度并分配内存。若要扩容,需要增加数组长度,并重新计算每个元素的散列值重新排放。

Python中,散列表和链表均使用数组方便直接调用内置函数。

代码示例

class MyHashSet:def __init__(self):# 初始化时创建散列表(固定大小的数组),散列表的每个元素也是一个数组(存储哈希映射结果冲突的key,动态增删元素)。其实就是一个二维数组。self.buckets = 10009 # 散列表长度self.HashSet = [[] for _ in range(self.buckets)]  # 注意这里要用列表生成式创建n个为空的数组,否则用[[]*n]只会创建1个[]。def hashfunc(self, key):return key % self.bucketsdef add(self, key: int) -> None:hashedkey = self.hashfunc(key)if key in self.HashSet[hashedkey]:returnself.HashSet[hashedkey].append(key)def remove(self, key: int) -> None:hashedkey = self.hashfunc(key)if key not in self.HashSet[hashedkey]:return self.HashSet[hashedkey].remove(key)def contains(self, key: int) -> bool:hashedkey = self.hashfunc(key)if key in self.HashSet[hashedkey]:return Trueelse:return False

算法复杂度分析

时间复杂度: O ( n m ) O(\frac{n}{m}) O(mn)。 n是待存储个数,m是散列表长度,寻找散列表中的元素位置只需找到下标为hashedvalue的元素即可,时间复杂度为 O ( 1 ) O(1) O(1),因此时间复杂度取决于遍历存放hashedvalue冲突的元素的数组时的时间消耗,这里为元素个数最多为 n m \frac{n}{m} mn,故时间复杂度为 O ( n m ) O(\frac{n}{m}) O(mn)
空间复杂度: O ( n + m ) O(n+m) O(n+m)。空间消耗为散列表+冲突处理数组。

可见,HashSet/HashMap是典型的权衡时间与空间消耗的数据结构算法。当m大时,时间消耗少,但空间消耗大;当m小时,时间消耗大,但空间消耗小。


http://www.ppmy.cn/devtools/15574.html

相关文章

Flutter开发好用插件url_launcher详解-启动 URL

文章目录 url_launcher介绍安装用法错误处理自定义行为其他功能 url_launcher介绍 url_launcher 是一个 Flutter 插件&#xff0c;用于启动 URL。它支持网络、电话、短信和电子邮件方案。您可以使用它从您的 Flutter 应用程序中打开网站、拨打号码、发送短信或撰写电子邮件。 …

【virtuoso】 PDK

什么是PDK&#xff1f; PDK( Process Design Kit )&#xff0c;工程设计数据包&#xff0c;是芯片厂家foundary提供给IC设计公司的有关制造工艺的模型和EDA工具支持。是连接IC制造公司&#xff0c;IC设计公司的桥梁。 PDK包含内容&#xff1a; 器件模型 SPICE模型模型 与 测量误…

Python Flask Web教程:make_response的详细用法

在 Flask 中,make_response 是一个非常实用的函数,它可以用来构造响应对象。下面是 make_response 函数的详细用法: 基本用法 在 Flask 中,make_response 可以用来从返回的数据中创建一个响应对象。它接受几种不同类型的参数,并返回一个 Response 对象。 from flask im…

好用、可靠有安全的企业局域网文件传输工具

在当今商业环境中&#xff0c;企业对于快速、安全的局域网(LAN)文件传输解决方案的需求不断攀升。选择恰当的工具对提升工作效率和保障数据安全至关重要&#xff0c;同时还能降低潜在的信息泄露风险。以下是企业在挑选局域网文件传输解决方案时应考虑的关键因素及其重要性的详细…

论文笔记;LargeST: A Benchmark Dataset for Large-ScaleTraffic Forecasting

Neurips 2023 1 intro 目前交通预测数据集的问题 规模小&#xff0c;通常只包含数百个节点和边在时间覆盖范围上存在严重不足&#xff0c;通常不超过6个月单个节点的元数据不足 ——> 提出了一个新的基准数据集LargeST 广泛的图大小&#xff0c;包括加利福尼亚州的8,600个…

Android binder 匿名服务实现双向通信

在binder 用户空间通信模型中&#xff0c;涉及client&#xff0c;server和servicemanager进程。一般来说&#xff0c;都是server注册服务到servicemanager中&#xff0c;client从servicemanager中获取服务&#xff0c;然后由client发起&#xff0c;使用服务中的方法。server都是…

iOS - 多线程-GCD

文章目录 iOS - 多线程-GCD1. 常见多线程方案2. GCD2.1 GCD的常见函数GCD中有2个用来执行任务的函数 2.2 GCD的队列2.2.1 GCD的队列可以分为2大类型 2.3 容易混淆的术语2.4.1 有4个术语比较容易混淆&#xff1a;同步、异步、并发、串行 2.4 各种队列的执行效果 3. 死锁3.1 死锁…

Stream流对list<map>的操作

Map<String,Object> map new HashMap<>();map.put("name","张三");map.put("age","30");map.put("sex","男");map.put("addr","深圳");List<Map<String,Object>> l…