哈希桶
概念
开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址(index = x % array.length()-1),具有相同地址的关键码归于同一子集合,每一个子集合称为一个哈希桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中
为了解决冲突问题,将冲突的元素串成一个链表来实现开散列
哈希表
顺序结构以及平衡树中,元素关键码与其存储位置之间没有对应的关系,因此在查找一个元素时,必须要经过关键码的多次比较。顺序查找时间复杂度为O(N),平衡树中为树的高度,即O( ),搜索的效率取决于搜索过程中元素的比较次数。
理想的搜索方法:可以不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素。
当向该结构中:插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)
例如:数据集合{1,7,6,4,5,9};
哈希函数设置为:hash(key) = key % capacity; capacity为存储元素底层空间总的大小。
如果这时候来了个44,是不是就冲突了呢?
于是就有了哈希桶来解决冲突,将index相同的串成一个链表
总的来说,当哈希表中的元素存了非常多的时候,表中的剩余位置越来越少,发生冲突的概率就越来越高,而且会影响探测的效率,哈希是追求高效查找的数据结构,而当哈希表中有效元素很多时,会大大影响效率。解决这个问题就需要哈希表中存储的元素不能太多,达到一定程序就要扩容,所以哈希表一定是不会存满的。具体什么情况下扩容,提到负载因子的概念,负载因子是有效元素/空间总数的值,有研究表明,线性探测负载因子达到75%左右就应该进行扩容
而实际上,当插入的元素比较特殊时,依然会导致大部分元素集中在一个链表中,导致某些链表特别长,这样一来,在哈希桶中查找元素实际上变成了在链表中查找元素,链表的查询时间复杂度是O(N),因此插入元素也应该避免这种问题的出现。闭散列当中是通过根据负载因子来进行扩容的方式解决问题的。哈希桶也是如此,哈希桶的规则中,当哈希桶中元素的个数与桶的个数相等时,就要考虑扩容,哈希桶的容量改变后,哈希函数计算的地址也改变了,所以要将旧哈希桶中的元素搬移到新的哈希桶中。
有时候会有些更极端的情况,虽然没达到扩容的条件,但是某些链表中挂载的节点已经非常多了,哈希表的性能因此会下降,此时的处理方法是当链表中的节点达到一定的阈值还没有得到扩容时,将链表转换成红黑树。一般设计是当链表中节点的个数等于8的时候,就将链表转换为红黑树,删除时,当红黑树中的节点个数小于6的时候,再将红黑树转换位链表。
简单实现
static class Node
{
int val;
int key;
Node next;
Node(int val,int key)
{
this.val=val;
this.key=key;
}
}
int usedSize=0;
public static final float DEFAULT_LOAD=0.75f;
Node[] arr=new Node[10];
public void resize()
{
Node[] tmp=new Node[arr.length*2];
for(int i=0;i<arr.length;i++)
{
Node cur=arr[i];
while(cur!=null)
{
Node curNext=cur.next;
int index=cur.key%tmp.length;
Node cur2=tmp[index];
while(cur2.next!=null)
{
cur2=cur2.next;
}
cur2.next=cur;
cur=curNext;
}
}
arr=tmp;
}
public void put(int key,int val)
{
Node node=new Node(val, key);
int index=key%arr.length;
Node cur=arr[index];
while(cur.next!=null)
{
if(cur.key==key)
{
return;
}
cur=cur.next;
}
cur.next=node;
usedSize++;
if((1.0f*usedSize/arr.length)>DEFAULT_LOAD)
{
resize();
}
}
public int get(int key)
{
int index=key%arr.length;
Node cur=arr[index];
while(cur!=null)
{
if(cur.key==key)
{
return cur.val;
}
cur=cur.next;
}
return -1;
}