算法3.5 基于拉链法的散列表
- 概述:将大小为M的数组中每个元素指向一条链表,链表中每个结点存储了散列值为该元素索引的键值对
- 基本思想:选择足够大的M,使所有链表都尽可能短,且发生冲突的元素都在链表中
- 查找:
- 根据散列值找到对应链表
- 沿着链表找到对应键
实现
使用了一般的链式类型SequentialSearchST扩展成散列表
//拉链法
public class SeparateChainHashST<Key, Value> {
private int N;//键值对总数
private int M;//散列表大小
private SeparateSearchST<Key, Value>[] st;
public SeparateChainHashST(){
this(997);//默认使用997条链表
}
public SeparateChainHashST(int M){
this.M = M;
st = (SeparateSearchST<Key, Value>[]) new SeparateSearchST[M];
for (int i = 0; i < M; i++)
st[i] = new SeparateSearchST();//需要给对象数组元素创建实例
}
private int hash(Key key){ return (key.hashCode()&0x7fffffff)%M; }
public Value get(Key key){ return (Value)st[hash(key)].get(key); }
public void put(Key key, Value val){ st[hash(key)].put(key, val);}
}
算法分析
- 链表的平均长度永远为N/M
- 命题:在M个链表,N个键的散列表中,(均匀散列假设成立时)任意一条链表中键的数量均在N/M的常数因子范围内的概率无限趋向于1
- 性质:M个链表,N个键的散列表中,未命中查找和插入操作所需的比较次数为~N/M
散列表的大小
- 选择适当的数组大小M,既不会浪费大量内存,也不会因为链表过长而增加查找时间
有序性操作
- 散列的目的在于均匀分布键值,因此顺序信息无法保存,一切有序性操作(最大最小键,范围查找等)都需要线性级别时间开销,但在顺序不重要的符号表中,散列表是最快,最广泛使用的