目录
哈希表
概念
当向该结构中:
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(或称为散列表)
例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity;capacity为存储元素底层空间的总大小
存储情况如下:
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快。而此时又衍生出来了一个问题,万一两个关键码通过函数计算的存放位置相同,该怎么办?这就涉及到了冲突。
冲突-概念
对于两个数据元素的关键字ki和kj(i!=j),有ki != kj,但有Hash(ki) == Hash(kj),即:不同关键字通过哈希函数计算出相同的哈希地址,这种现象称为哈希冲突或哈希冲撞 。
把具有不同关键码而具有相同哈希地址的数据元素称为“同义词”。
冲突-避免
冲突-避免-哈希函数设计
常见哈希函数(常用)
注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突
冲突-避免-负载因子的调节
负载因子和冲突率的关系粗略演示
所以当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来降低冲突率。
已知哈希表中已有关键字个数是不可变的,那么我们只能调整哈希表中数组的大小。
解决哈希冲突的两种常见方法有:闭散列和开散列。
冲突-解决-闭散列
1.线性探测
插入
2.二次探测
因此:闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷。
冲突-解决-开散列
方法如图所示:
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
开散列,可以认为是把一个大集合中的搜索问题转化为在小集合中搜索了。
哈希桶的实现
下面是基于key-value模型写的哈希桶部分方法的代码:
//key-value模型
public class HashBucket {
private static class Node {
private int key;
private int value;
Node next;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
}
//用数组表示哈希表
private Node[] array;
//当前哈希表元素个数
private int size;
//定义荷载因子
private static final double LOAD_FACTOR = 0.75;
public int put(int key, int value) {
//根据哈希函数确定存放的下标
int index = key % array.length;
//在链表中查找key所在的结点
//如果找到了,更新
//所有节点都不是key,插入一个新的结点
for (Node cur = array[index]; cur != null; cur = cur.next) {
if(key == cur.key) {
int oldValue = cur.value;
cur.value = value;
//返回更新前key对应的value
return oldValue;
}
}
//链表遍历完成,没有找到这个key
Node node = new Node(key, value);
node.next = array[index];
array[index] = node;
size++;
if(loadFactor() >= LOAD_FACTOR) {
resize();
}
return -1;
}
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) {
//利用tmp记录cur的位置
Node tmp = cur.next;
//计算元素在新数组中的位置
int newIndex = cur.key % newArray.length;
//头插法
cur.next = newArray[newIndex];
newArray[newIndex] = cur;
//回溯cur的位置
cur = tmp;
}
}
//将新数组赋值给原数组
array = newArray;
}
//计算当前荷载因子的大小
private double loadFactor() {
return size * 1.0 / array.length;
}
public HashBucket() {
array = new Node[8];
size = 0;
}
//get方法
public int get(int key) {
int index = key % array.length;
Node head = array[index];
Node cur = head;
while(cur != null) {
if(key == cur.key) {
return cur.value;
}
cur = cur.next;
}
//未找到,则返回-1
return -1;
}
}