开放地址法:设计哈希集合
Date Created: Mar 13, 2021 6:02 PM
Status: 要学习的
官方题解给了链式搜索的方式,也提到了另两种方式解决哈希冲突。这面想通过开放地址法来实现一下。
开放地址的方式正如官方给出的解释:
当发现哈希值 h 处产生冲突时,根据某种策略,从 h 出发找到下一个不冲突的位置。例如,一种最简单的策略是,不断地检查 h+1,h+2,h+3,… 这些整数对应的位置。
其中关键的就是如何定义策略。
解释中提到的不断的加一直到找到对应内容的方式。可以通过叫线性试探法来实现。
那么首先定义一下需要存储数据的容器。需要考虑到容器的大小如何设置:这面设置为10000。因为题目中 ‘最多调用 10的4次方 次 add、remove 和 contains 。’ 先理解为add和remove及contains调用次数总和是小于等于1w的。那么做多存放1w个数。
private int[] data = new int[10000];
那么为了保证添加的数字都会映射到这个数组区间内,需要将入参取模处理。
int i = key % 10000;
那么如何解决哈希冲突(1%10000和10001%10000 这两个入参都指向了同一个地址)呢?
i = (i + 1) % 10000; //这样如果i这个位置有值,就在重新获取一下通过加一的方式
下面先简单的连起来写一遍
class MyHashSet {
private int[] data;
private static final int SIZE = 10000;
public MyHashSet() {
data = new int[SIZE];
Arrays.fill(data, -1); //-1代表未赋值哈 (入参取值范围为[0,1000000])
}
public void add(int key) {
data[hash(key)] = key;
}
public void remove(int key) {
data[hash(key)] = -1;
}
public boolean contains(int key) {
return data[hash(key)] != -1;
}
private int hash(int key) {
int i = key % SIZE;
while (data[i] != key && data[i] != -1) {
i = (i + 1) % SIZE;
}
return i;
}
}
上面的代码是存在问题的。
问题描述为:当输入add(1),add(10001),remove(1),contains(10001) 会返回false。
原因为 remove 1后把1这个位置置为-1,判断10001 的时候误以为 1 这个位置,没有值。自然不会再次加一来找10001 的真实位置2了哈。(因为2是之前因为1这个位置被占用了,才往前移动了一个。现在1这个位置为空了,导致不再往下寻找10001的位置了)
所以咱们要添加一个标识,表示 虽然当前内容被移除了,但是还需要通过它找到曾经以为它的存在而被移动到其他位置上的内容。这面可以添加-2来表示。
修改一下代码
class MyHashSet {
private int[] data;
private static final int SIZE = 10000;
public MyHashSet() {
data = new int[SIZE];
Arrays.fill(data, -1); //-1代表未赋值哈 (入参取值范围为[0,1000000])
}
public void add(int key) {
data[hash(key)] = key;
}
public void remove(int key) {
data[hash(key)] = -2;
}
public boolean contains(int key) {
return data[hash(key)] != -1;
}
private int hash(int key) {
int i = key % SIZE;
while (data[i] != key && data[i] != -1) { //当前位置为-2的时候也会进入循环体内
i = (i + 1) % SIZE;
}
return i;
}
}
好的,可以提交代码啦。