hash表



散列法的基本思想是把键分布在一个称为散列表的一维数组H[0.......m-1]中,我们可以对每个键计算某些被称为散列函数的预定义函数h的值,来完成这种分布。该 函数为每个键确定它在H中的下标

一般来说,hash函数的要求:

1)使得键在hash表里尽可能均匀的分布

2)hash函数必须容易计算


显然,如果选择的散列表长度m小于键的数量n,则会 碰撞。这是一种2个或多个键被hash到散列表的同一个单元格的情况。即使m相对于n足够大,这种情况仍会发生。

2种解决碰撞的方法:开散列(分离链)和闭散列(开放地址),以下分别介绍,并用开散列的方式模拟实现一个hash表。


----------------------------------------------------------------------------------------------------------------------------------------------------

1,开散列解决冲突(分离链)

这种方式将同一hash值的键用一个链表连接起来。如下图所示:

2011062614451494.jpg

假设我们构造的hash函数使得ARE和SOON的hash值都是11(如上图),那么他们都存在值为11处的链表中。


hash表上的操作:查询,插入,删除

先介绍一个概念:负载因子,如果一个散列函数比较均匀的将n个键分布在散列表的m个单元格中,那么每个单元格对应的链表平均长度就是n/m (即平均每个hash值都有n/m元素冲突),我们称n/m为负载因子α。

查询:负载因子α的值(即平均每个链表的长度)决定了查询的效率

插入和删除:在链表里插入和删除。

我们希望负载因子α和1不要相差太大,如果太小将意味着非常多的空链表,因而没有好好地利用空间,如果太大将意味着平均每个链表长度很长,查询效率很低。


--------------------------------------------------------------------------------------------------------------------------------------------------


2,闭散列


在闭散列中,所有键都存储在散列表本身中,而没有使用链表。(当然,这意味这表的长度至少必须和键的数量一样大,而分离链的方式表的长度可以比键数量小)。

可以采用不同的策略来解决碰撞,最简单的一种称为 线性探查,它检查碰撞发生处后面的单元格,如果该单元格为空,新的键就放在那里,如果下一个单元格也被占用了,就继续检查下下一个单元格(检查到尾部的时候返回头部,把数组看做循环的),以此类推,直到找到一个可用单元格。

闭散列上的查找,插入,删除:

查找:对于键K,计算哈希值为h(K),如果单元格h(K)处为空,查找失败,否则查找下一个单元格,直到遇到匹配滴(查找成功)或遇到一个空单元格(查找失败)

插入:上面已描述

删除:跟查找和插入不一样!如果你简单的把一个键从散列表中删除,将会导致它后面的键可能查询不到(因为查到被删除的这个地方的时候查找就结束了),一种办法是 延迟删除,用一个标记来标记该位置曾被占用过,但现在实际已删除了,到下一次可以插入到它的时候再把原来的值覆盖。


散列表将满的时候闭散列容易产生 聚类的现象:一系列连续的单元格被占据(查找效率滴)。

改进散列函数,和二次散列都可以改进闭散列的性能。


--------------------------------------------------------------------------------------------------------------------------------------------------


3,模拟hash表的实现(分离链解决冲突)

实现一个hash表:

基本操作:查询,插入,删除

注意要用链表把元素拉起来,就需要一个节点Node类,把元素放在Node里:

 
  
package Section7;

public class HashNode {
// hash链里面的结点
public int element;
public HashNode next;

public HashNode( int element)
{
this .element = element;
next
= null ;
}

}


hash表里用一个数组存放每个链表的第一个节点,还要记录当前的节点数:

 
  
private HashNode[] hashtable; // 在hashtable数组里面存放的是第一个顶点,冲突顶点链接到后面
private int count; // 记录hash表中实际有的结点数目


完整的结构:

 
  
public class HashTable {

/**
*
@param args
*/
private HashNode[] hashtable; // 在hashtable数组里面存放的是第一个顶点,冲突顶点链接到后面
private int count; // 记录hash表中实际有的结点数目

public HashTable( int n){
// 哈希表的大小
hashtable = new HashNode[n];
count
= 0 ;
}

public int hash( int x){
// hash函数
return x % 11 ;
}

public int size(){
return count;
}

public void insertHash( int x){

}

public int deleteHash( int x){

}


public int searchHash( int x){

}


public static void main(String[] args){

}


哈希表上的操作:查询,插入,删除,其实就是根据hash值在链表里进行链表的插入,删除,查询操作,都不难,就是删除还比较麻烦,情况比较多,注意一下:

查询:

 
  
public int searchHash( int x){
// 在hash表中查找元素x的下标,若没有,返回-1
int hashValue = hash(x);
if (hashtable[hashValue] == null ) // 如果链表为空
return - 1 ;
else // 链表不空
{
HashNode lastNode
= hashtable[hashValue];
while (lastNode != null )
{
if (lastNode.element == x)
return hashValue;
lastNode
= lastNode.next;
}
}
return - 1 ;
}


插入:

 
  
public void insertHash( int x){
// 向hash表插入元素x
int hashValue = x % 11 ;
HashNode node
= new HashNode(x);
if (hashtable[hashValue] == null )
hashtable[hashValue]
= node;
else
{
HashNode lastNode
= hashtable[hashValue];
while ( null != lastNode.next)
lastNode
= lastNode.next;
lastNode.next
= node;
}
count
++ ;
}



删除:

 
  
public int deleteHash( int x){
// 在hash表中删除元素x,并返回x的下标,若x不存在返回-1
int hashValue = hash(x);
if (hashtable[hashValue] == null )
return - 1 ;
else // 至少一个元素
{
if (hashtable[hashValue].next == null ) // 如果只有一个元素
{
if (hashtable[hashValue].element == x)
{
hashtable[hashValue]
= null ;
count
-- ;
return hashValue;
}
}
else // 链表里至少2个元素
{
if (hashtable[hashValue].element == x) // 第一个元素就相等
{
hashtable[hashValue]
= hashtable[hashValue].next;
count
-- ;
return hashValue;
}
else // 第一个元素不相等
{
HashNode preNode
= hashtable[hashValue],currentNode = preNode.next;
while (currentNode != null )
{
if (currentNode.element == x)
{
preNode.next
= currentNode.next;
count
-- ;
return hashValue;
}
preNode
= currentNode;
currentNode
= currentNode.next;
}
}
}
}
return - 1 ;
}



--------------------------------------------------------------------------------------------------------------------------------------------------



总结:

模拟hash表的实现



转载于:https://www.cnblogs.com/jmzz/archive/2011/06/26/2090647.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值