前言
hashSet拆分即hash & set,设计hash需要确定hash函数 + 存数据的数组 + hash冲突的解决办法。对于set,hash的key天然的不重复,所以直接用key作为set的元素即保证不重复性。
一、设计hash集合
二、hash函数+数组+拉链法
1、常规设计
// 设计一个hashSet
public class MyHashSet {
Node[] table;
private final static int N = 769;// 取一个质数作为数组长度,减少冲突。
public MyHashSet() {
table = new Node[N];
// 设置头节点,统一操作。
for (int i = 0; i < N; i++) table[i] = new Node();
}
// 三个操作都是:找key映射到的头节点,然后链表寻找是否有对应的key.
public void add(int key) {
int h = hash(key);
Node p = table[h];
while (p.next != null) {
// 有此key,就不用再添加了。
if (key == p.next.key) return;
p = p.next;
}
Node n = new Node();
n.key = key;
p.next = n;
}
public void remove(int key) {
int h = hash(key);
Node p = table[h];
while (p.next != null) {
// 有此key,才把其移除掉,并break出去。
if (p.next.key == key) {
p.next = p.next.next;
break;
}
p = p.next;
}
}
public boolean contains(int key) {
int h = hash(key);
Node p = table[h];
while (p.next != null) {
// 找到就返回。
if (p.next.key == key) return true;
p = p.next;
}
return false;
}
// 对key进行扰乱,再对质数取余。
private int hash(int key) {
key = (key >>> 16) ^ key;
return key % N;
}
// hash冲突时,用拉链法解决。
class Node {
int key;
Node next;
}
}
2、常见的数组hash
class MyHashSet {
final static int N = 1000000;
int[] fx = new int[N + 1];
public MyHashSet() {
}
public void add(int key) {
fx[key] = 1;
}
public void remove(int key) {
// 是1就变0,是0就不管。
fx[key] = 0;
}
public boolean contains(int key) {
return fx[key] == 1;
}
}
总结
1)hash三要素:hash函数 + 存数据的数组 + hash冲突的解决方式。
2)hash函数有很多,比如线性法/平方法,hash冲突的方式也有很多,拉链法/线性探测法/平方探测法等等,以后算法能力提高了,还可以用红黑树数据结构来解决冲突,降低搜索时间复杂度。