一、哈希表
我们在学习搜索时,理想的搜索方法是不经过任何比较,一次直接从表中得到要搜索的元素。哈希表就是通过哈希函数(Hash Function)使得元素的存储位置与他的值val能够一一对应。
1.哈希函数
HashFunc(key) = key % array.length
2.哈希冲突
当两个不同key通过哈希函数得到的哈希地址是一样的,这种情况我们就把它称为哈希冲突或者哈希碰撞。
3.解决哈希冲突(闭散列)
闭散列也称为开放定址法,下面介绍几种闭散列。
当发生冲突即求得的位置已经有元素时,我们可以通过依次往后寻找空缺的位置的方法来解决问题,这种方法称为线性探测法。
我们还可以进行二次哈希(二次探测),
H(i) = H(0) + i ^ 2 H(0)表示第一此计算出来的哈希地址,i =1,2,3······
相对于线性探测,这样的好处是避免产生冲突的数据堆积在一起。
4.解决哈希冲突(开散列)
闭散列的弊端是空间利用率很低,我们可以使用开散列的方法,将哈希地址相同的数据归于一个集合,通过链表连接起来。这种方法也称为哈希桶
下面 是代码实现:
public class HashBuck {
static class Node {
public int key;
public int val;
public Node next;
public Node(int key, int val) {
this.key = key;
this.val = val;
}
}
public Node[] array = new Node[10];
public int usedSize;
public static final double LOAD_FACTOR = 0.75; //负载因子最大值
public void push(int key, int val) {
Node node = new Node(key, val);
int index = key % array.length;
Node cur = array[index];
while (cur != null) {
if(cur.key == key) {
//更新val
cur.val = val;
return;
}
cur = cur.next;
}
node.next = array[index];
array[index] = node;
usedSize++;
if(doLoadFactor() >= 0.75) {
//处理了 重新哈希
reSize();
}
}
private double doLoadFactor() {
return usedSize*1.0 / array.length;
}
private void reSize() {
Node[] newArray = new Node[array.length*2];
//处理重新哈希
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while (cur != null) {
int index = cur.key % newArray.length;
//记录下来 之前的cur.next 不然修改后就找不到cur.next了
Node curNext = cur.next;
//进行头插法,插入到新数组
cur.next = newArray[index];
newArray[index] = cur;
cur = curNext;
//尾插法
/*Node cur2 = newArray[index];
if(cur2 == null) {
newArray[index] = cur;
} else {
while(cur2.next != null){
cur2 = cur2.next;
}
cur2.next = cur;
}
cur = curNext;*/
}
}
//把数据给到原数组 array
array = newArray;
}
public int get(int key) {
//1、找到位置
int index = key % array.length;
//2、遍历数组
Node cur = array[index];
while (cur != null) {
if(cur.key == key) {
return cur.val;
}
cur = cur.next;
}
return -1;
}
}