目录
哈希表实现代码已上传至gitee中:点击查看代码
哈希表
哈希表(也称散列表):是根据关键码值而直接进行访问的数据结构。也就是说,它通过把关键码值映射到表中的一个位置来访问,以加快查找速度。这个映射函数叫做哈希函数,存放记录的数组叫哈希表.
例如集合{1,7,8,9,4,6}
哈希函数设置为:hash(key) = key % capacity,capacity = 10(capacity为存储元素底层空间大小)
则该哈希表为:
哈希冲突:不同关键字通过相同的哈希函数计算出相同的哈希地址,这种现象称为哈希冲突或哈希碰撞.
如何避免哈希冲突:
①设计合理的哈希函数
常见的哈希函数:
直接定址法、除留余数法、平方取中法、折叠法、随机数法、数学分析法
②负载因子调节;
③闭散列法(线性探测、二次探测)
③开散列法(拉链法)
哈希表的实现
该哈希表的实现以开散列法和负载因子的调节来处理哈希冲突
初始化
默认负载因子DEFAULT_LOAD_FACTOR = 0.75
默认初始哈希表长度为8
usedSize用来记录哈希表中存储的数据元素,以便后续进行负载因子的计算
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;
public int usedSize;//记录哈希表中存储的数据元素个数
public static final double DEFAULT_LOAD_FACTOR = 0.75;//默认的负载因子
public HashBuck() {
array = new Node[8];//初始化哈希表长度为8
}
}
计算负载因子
负载因子的计算为哈希表中目前存储的元素个数/哈希表长度
private double loadFactor() {
return this.usedSize*1.0 / this.array.length;
}
哈希表的扩容
哈希表的扩容默认为二倍扩容,扩容方式为:新建一个数组(哈希表),来重新存储当前哈希表中的元素.
private void resize() {
Node[] tmp = new Node[array.length*2];
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while(cur != null) {
int newIndex = cur.key % tmp.length;
Node curNext = cur.next;
cur.next = tmp[newIndex];
tmp[newIndex] = cur;
cur = curNext;
}
}
array = tmp;
}
哈希表的插入
进行插入操作的步骤:
①首先利用哈希函数确定hash值,为了简化实现,capacity直接取的是哈希表长度;
②找到hash值对应的哈希表位置,由于采取的是开散列法,则利用头插法将该值插入对应位置
③插入成功后计算当前的负载因子,如果此时计算得出的负载因子大于默认的负载因子,则进行扩容操作
public boolean insert(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) {
cur.val = val;
return false;
}
cur = cur.next;
}
//利用头插法插入
node.next = array[index];
array[index] = node;
this.usedSize++;
if(loadFactor() >= DEFAULT_LOAD_FACTOR) {
resize();
}
return true;
}
查找元素
利用哈希函数确定hash值,根据hash值在对应的下标位置寻找待查找的值,若查找到则返回查找的值,若未查找到,则返回-1.
public int get(int key) {
int index = key % array.length;
Node cur = array[index];
while(cur != null) {
if(cur.key == key) {
return cur.val;
}
cur = cur.next;
}
return -1;
}
完整代码
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;
public int usedSize;//记录哈希表中存储的数据元素个数
public static final double DEFAULT_LOAD_FACTOR = 0.75;//默认的负载因子
public HashBuck() {
array = new Node[8];
}
/**
* 哈希桶的插入
* @param key
* @param val
* @return 插入成功返回true,反之返回false
*/
public boolean insert(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) {
cur.val = val;
return false;
}
cur = cur.next;
}
//利用头插法插入
node.next = array[index];
array[index] = node;
this.usedSize++;
if(loadFactor() >= DEFAULT_LOAD_FACTOR) {
resize();
}
return true;
}
/**
* 扩容,二倍扩容
*/
private void resize() {
Node[] tmp = new Node[array.length*2];
for (int i = 0; i < array.length; i++) {
Node cur = array[i];
while(cur != null) {
int newIndex = cur.key % tmp.length;
Node curNext = cur.next;
cur.next = tmp[newIndex];
tmp[newIndex] = cur;
cur = curNext;
}
}
array = tmp;
}
/**
* 查找元素
* @param key
* @return 查找成功返回待查找的值,反之返回-1
*/
public int get(int key) {
int index = key % array.length;
Node cur = array[index];
while(cur != null) {
if(cur.key == key) {
return cur.val;
}
cur = cur.next;
}
return -1;
}
/**
* 计算当前哈希桶的负载因子
* @return
*/
private double loadFactor() {
return this.usedSize*1.0 / this.array.length;
}
}
测试用例
public class TestDemo {
public static void main(String[] args) {
HashBuck hashBuck = new HashBuck();
hashBuck.insert(1,1);
hashBuck.insert(7,7);
hashBuck.insert(8,8);
hashBuck.insert(9,9);
hashBuck.insert(4,4);
hashBuck.insert(6,6);
System.out.println(hashBuck.usedSize);
System.out.println(hashBuck.get(6));
}
}
Debug调试显示结果证明已成功实现简单的哈希表