设计哈希集合 && 设计哈希映射

题目链接:leetcode 705 leetcode 706

题目描述

leetcode 705 设计哈希集合

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

实现 MyHashSet 类:

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

提示:

  • 0 <= key <= 106
  • 最多调用 104addremovecontains

leetcode 706 设计哈希映射

不使用任何内建的哈希表库设计一个哈希映射(HashMap)。

实现 MyHashMap 类:

  • MyHashMap() 用空映射初始化对象
  • void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value 。
  • int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
  • void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。

提示:

  • 0 <= key, value <= 106
  • 最多调用 104putgetremove 方法

题解

哈希函数和冲突处理是最基本的数据结构,教材里都会讲到:

  • 哈希函数:能够将集合中任意可能的元素映射到一个固定范围的整数值,并将该元素存储到整数值对应的地址上。
  • 冲突处理:由于不同元素可能映射到相同的整数值,因此需要在整数值出现「冲突」时,需要进行冲突处理。总的来说,有以下几种策略解决冲突:
    • 链地址法:为每个哈希值维护一个链表,并将具有相同哈希值的元素都放入这一链表当中。
    • 开放地址法:当发现哈希值 h 处产生冲突时,根据某种策略,从 h 出发找到下一个不冲突的位置。例如,一种最简单的策略是,不断地检查 h+1,h+2,h+3… 这些整数对应的位置。
    • 再哈希法:当发现哈希冲突后,使用另一个哈希函数产生一个新的地址。
    • 扩容:当哈希表元素过多时,冲突的概率将越来越大,而在哈希表中查询一个元素的效率也会越来越低。因此,需要开辟一块更大的空间,来缓解哈希表中发生的冲突。

下面我们使用链地址法来进行处理:

设哈希表的大小为 base,则可以设计一个简单的哈希函数:$\text{hash}(x) = x \bmod \textit{base}$。

我们开辟一个大小为 base 的数组,数组的每个位置是一个链表。当计算出哈希值之后,就插入到对应位置的链表当中。

由于我们使用整数除法作为哈希函数,为了尽可能避免冲突,应当将 base 取为一个质数。在这里,我们取 $\textit{base}=857$。

leetcode 705 设计哈希集合

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
class MyHashSet {
private static final int base = 857;
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>();
}
}

private static int hash(int k) {
return k % base;
}

public void add(int key) {
int pos = hash(key);
Iterator<Integer> it = data[pos].iterator();
while (it.hasNext()) {
Integer element = it.next();
if (element == key) {
return;
}
}
data[pos].offerLast(key);
}

public void remove(int key) {
int pos = hash(key);
Iterator<Integer> it = data[pos].iterator();
while (it.hasNext()) {
Integer element = it.next();
if (element == key) {
data[pos].remove(element);
return;
}
}
}

/**
* Returns true if this set contains the specified element
*/
public boolean contains(int key) {
int pos = hash(key);
Iterator<Integer> it = data[pos].iterator();
while (it.hasNext()) {
Integer element = it.next();
if (element == key) {
return true;
}
}
return false;
}
}

leetcode 706 设计哈希映射

「设计哈希映射」与「设计哈希集合」解法接近,唯一的区别在于我们存储的不是 $\textit{key}$ 本身,而是 $(\textit{key}, \textit{value})$ 对。除此之外,代码基本是类似的。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
class MyHashMap {
class Pair {
int key;
int value;

public Pair(int key, int value) {
this.key = key;
this.value = value;
}

public int getKey() {
return key;
}

public int getValue() {
return value;
}

public void setValue(int value) {
this.value = value;
}
}

private static final int base = 857;
private LinkedList[] data;

/**
* Initialize your data structure here.
*/
public MyHashMap() {
data = new LinkedList[base];
for (int i = 0; i < base; i++) {
data[i] = new LinkedList<Pair>();
}
}

private static int hash(int k) {
return k % base;
}

/**
* value will always be non-negative.
*/
public void put(int key, int value) {
int pos = hash(key);
Iterator<Pair> it = data[pos].iterator();
while (it.hasNext()) {
Pair element = it.next();
if (element.key == key) {
element.setValue(value);
return;
}
}
data[pos].offerLast(new Pair(key, value));
}

/**
* Returns the value to which the specified key is mapped, or -1 if this map contains no mapping for the key
*/
public int get(int key) {
int pos = hash(key);
Iterator<Pair> it = data[pos].iterator();
while (it.hasNext()) {
Pair element = it.next();
if (element.key == key) {
return element.value;
}
}
return -1;
}

/**
* Removes the mapping of the specified value key if this map contains a mapping for the key
*/
public void remove(int key) {
int pos = hash(key);
Iterator<Pair> it = data[pos].iterator();
while (it.hasNext()) {
Pair element = it.next();
if (element.key == key) {
data[pos].remove(element);
return;
}
}
}
}

参考

设计哈希集合

设计哈希映射

坚持原创技术分享,感谢您的支持和鼓励!