hash表



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

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

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

2)hash函数必须容易计算


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

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


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

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

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



假设我们构造的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表的实现



posted @ 2011-06-26 15:00  jinmengzhe  阅读(1758)  评论(1编辑  收藏  举报