哈希表可以表述为,是一种可以根据关键字快速查询数据的数据结构
目录
一. 哈希表有哪些优点?
不论哈希表中数据有多少,增加,删除,改写数据的复杂度平均都是O(1),效率非常高
二. 实现哈希表
1. 哈希表原理
如果说每一个数据它都对应着一个固定的位置,那我们查找特定一个数据时,就可以直接查看这个数据对应的位置是否存在数据。一个形象的例子就是学生在教室中的位置,开学的时候,老师会给学生每一个人分配一个位置,而且不允许学生随便乱坐位置,以后老师要查看今天李刚同学有没有上课,直接看李刚同学的位置是不是有人就可以判断,没必要点了全班同学的名才可以知道李刚同学来了没有。
2. 实现简单的哈希表
根据上面的原理,首先,我们要分配一片空间用来存储我们数据,比如是一个空的数组
然后,有数据存进来的时候,按照特定规则得出这个数据在数组中的位置,将数据存进这个位置
我们就以存进一个整型数据为例,特定规则就是取余
根据计算出来的值,将这些数据放入对应的位置,我们的数组变为
我们已经把数据插入到了哈希表中,现在,我们要查找一个数据,只要按照取余规则计算出这个数据在数组中对应的位置,然后查看数组的这个位置,就可以取出这个数据了,比如我们要从哈希表中取出52,根据取余规则,52的计算出来的位置是8,数组中8这个位置是空的,52不在哈希表中,找不到52的数据;从哈希表中取出77,77计算出来的位置是0,数组中0这个位置有值,而且值就是77,从哈希表中取出77的值。
至此,我们知道实现了一个很简单的哈希表的原理,其实还存在很多问题,这个我们接下来讨论,这儿先把我们前面的一些概念用专业的术语替换一下,前面我们所说的特定规则,我们称之为哈希函数,用特定股则计算出来的值称之为哈希值。
3. 哈希表的代码实现
package datastructure.hash;
import java.util.Arrays;
import java.util.NoSuchElementException;
public class MyHashMap {
private class Node{
private int key;
private int value;
Node next;
public Node(int key, int value, Node next) {
this.key = key;
this.value = value;
this.next = next;
}
}
private int size;
//默认的哈希表的长度
private static final int DEFSAULT_CAPACITY=16;
//默认负载因子
private static final double LOAD_FACTOR=0.75;
//取模用于取得索引
private int M;
//实际存储的数组
private Node[]data;
public MyHashMap(){
this(DEFSAULT_CAPACITY);
}
public MyHashMap(int initCAp){
this.data=new Node[initCAp];
this.M=initCAp;
}
//哈希函数
public int hash(int key){
return Math.abs(key)%M;
}
//在当前哈希表添加一个键值对key=value
public int add(int key,int value){
//先对key取模,取得所在的索引
int index=hash(key);
//遍历链表找到所在的索引,查看当前的key是否存在
for (Node i=data[index];i!=null;i=i.next){
if (i.key==key){
int oldVal=i.value;
i.value=value;
return oldVal;
}
}
//此时链表中没有,新建头节点插入
Node node=new Node(key,value,data[index]);
data[index]=node;
size++;
//添加一个元素后,查看是否需要扩容
if (data.length*DEFSAULT_CAPACITY<=size){
resize();
}
return value;
}
private void resize() {
//新数组长度变为原来的一倍
Node []nweData=new Node[data.length<<1];
//原节点的key对应新数组的索引
//现在取得模数因改变为新数组的长度
for (int i=0;i<=data.length;i++){
if (data[i]!=null){
for (Node x=data[i];x!=null;x=x.next){
Node next=x.next;
int newIndex=hash(x.key);
x.next=nweData[newIndex];
nweData [newIndex]=x;
x=next;
}
}else {
continue;
}
}
data=nweData;
}
//删除哈希表中key对应的节点
public int remove(int key){
//判断头节点是不是待删除的节点
int indx=hash(key);
Node head=data[indx];
if (head.key==key){
int val=head.value;
data[indx]=head.next;
head.next=head=null;
size--;
return val;
}
//当前链表的头节点不是待删除的节点
Node pre=head;
while (pre.next!=null){
if (pre.next.key==key){
//pre是待删除节点的前驱
int val=pre.next.value;
pre.next=pre.next.next;
pre.next.next=pre.next=null;
size--;
return val;
}
}
throw new NoSuchElementException("no such key!");
}
//查询Key是否在哈希表中
public boolean containsKey(int key){
int idex=hash(key);
for (Node x=data[idex];x!=null;x=x.next){
if (x.key==key){
return true;
}
}
return false;
}
public boolean containsValue(int value){
//遍历哈希表
for (int i=0;i<size;i++){
for (Node x=data[i];x!=null;x=x.next){
if (x.value==value){
return true;
}
}
}
return false;
}
}