题目介绍
力扣705题:leetcode-cn.com/problems/de…
方法一:简单数组
由于题目给出了 0 <= key <= 10^6 数据范围,同时限定了 key 只能是 int。
我们可以直接使用一个 boolean 数组记录某个 key 是否存在,key 直接对应 boolean 的下标。
代码如下:
class MyHashSet {
boolean[] nodes = new boolean[1000009];
/**
* 添加元素
*/
public void add(int key) {
nodes[key] = true;
}
/**
* 移除元素
*/
public void remove(int key) {
nodes[key] = false;
}
/**
* 判断元素是否在数组中
*/
public boolean contains(int key) {
return nodes[key];
}
}
方法二:链地址法
为了实现哈希集合这一数据结构,有以下几个关键问题需要解决:
-
哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上。
-
冲突处理:由于不同元素可能映射到相同的整数值,因此需要在整数值出现「冲突」时,需要进行冲突处理。总的来说,有以下几种策略解决冲突:
- 链地址法:为每个哈希值维护一个链表,并将具有相同哈希值的元素都放入这一链表当中。
- 开放地址法:当发现哈希值 h 处产生冲突时,根据某种策略,从 h 出发找到下一个不冲突的位置。例如,一种最简单的策略是,不断地检查 h + 1, h + 2, h + 3,… 这些整数对应的位置。
- 再哈希法:当发现哈希冲突后,使用另一个哈希函数产生一个新的地址。
-
扩容:当哈希表元素过多时,冲突的概率将越来越大,而在哈希表中查询一个元素的效率也会越来越低。因此,需要开辟一块更大的空间,来缓解哈希表中发生的冲突。
设哈希表的大小为 base,则可以设计一个简单的哈希函数:hash(x)=x mod base。
我们开辟一个大小为 base 的数组,数组的每个位置是一个链表。当计算出哈希值之后,就插入到对应位置的链表当中。
由于我们使用整数除法作为哈希函数,为了尽可能避免冲突,应当将 base 取为一个质数。在这里,我们取 base=769。
代码如下:
class MyHashSet {
private static final int BASE = 769;
private LinkedList[] data;
/** Initialize your data structure here. */
public MyHashSet() {
data = new LinkedList[BASE];
for (int i = 0; i < BASE; ++i) {
data[i] = new LinkedList<Integer>();
}
}
public void add(int key) {
int h = hash(key);
Iterator<Integer> iterator = data[h].iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
//判断是否已经存在key,如果已经存在则直接返回,什么也不做
if (element == key) {
return;
}
}
//将key添加到链表的末尾
data[h].offerLast(key);
}
public void remove(int key) {
int h = hash(key);
Iterator<Integer> iterator = data[h].iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element == key) {
data[h].remove(element);
return;
}
}
}
/** Returns true if this set contains the specified element */
public boolean contains(int key) {
int h = hash(key);
Iterator<Integer> iterator = data[h].iterator();
while (iterator.hasNext()) {
Integer element = iterator.next();
if (element == key) {
return true;
}
}
return false;
}
private static int hash(int key) {
//取模的方式
return key % BASE;
}
}
复杂度分析
-
时间复杂度:O(n / b)。其中 n 为哈希表中的元素数量,b 为链表的数量。假设哈希值是均匀分布的,则每个链表大概长度为 n / b。
-
空间复杂度:O(n+b)。