1、概述
集合中的元素按以下两种顺序之一排列:
- 其顺序由往集合添加和(或)从集合中删除元素的顺序决定,如栈、队列、无序列表和索引表
- 其顺序由比较元素(或是元素的某个关键字部分)的值决定,如有序列表和二叉查找树
在散列中,元素被存储在一个散列表中,他们在散列表中的位置由一个散列函数决定。散列表中的每个位置可以称为单元(cell)或桶(bucket)。
与集合的实现不同,使用散列方法访问特定元素的时间与散列表中的元素数目无关,时间复杂度都是O(1)。但是只有当每个元素都映射到散列表中独特位置时,其效率才会得到完全体现。将每个元素映射到散列表中独特位置的散列函数称为完美散列函数。两个元素或关键字映射到散列表中相同位置的情况称为冲突(collision)。
散列表的大小:
- 如果已知数据集合的大小并且可以运用完美散列函数,那么只需将散列表设置为数据集合的大小。
- 如果完美散列函数根本不存在或者不适用,但数据集合的大小已知,将散列表的大小设置为数据集合大小的1.5倍。
- 如果不知道数据集合的大小,要动态改变散列表的大小。一种是散列表满的时候才扩大,但当散列表满时其性能严重下降。一种是使用负载因子(load factor)。
2、散列函数
- 余数法:把关键字除以一个正整数的余数作为给定元素的索引。
- 折叠法:把关键字分割成多个部分,再组合或叠加到一起创建散列表的索引。
- 平方取中法:平方后,取中间合适的位数作为索引。
- 基数转换法:将关键字转换为另一种进制,再用余数法。
- 数字分析法:索引是通过抽取、处理关键字指定数位来生成的。
- 长度相关法:关键字和关键字的长度用某种方式绑定
3、解决冲突
- 链地址法:链地址法有多种实现方式。
- 让保存散列表的数组大于散列表的单元数,把多余的空间作为溢出区。在这中方式中,数组每个位置同时存储了一个元素以及下一个元素的数组索引
- 使用链表
- 将散列表每个位置作为一个指针,指向一个集合
- 开发地址法
- 线性探测法:如果一个元素散列到位置p而p已经被占用,就尝试位置(p+1)%s,这里s是散列表的大小,直到找到一个空位置或回到p为止。
- 二次探测法:newhashcode(x) = hashcode(x) +((-1)^(i-1))*(((i+1)/2)^2),i从1到s-1。查找序列为p、p+1、p-1、p+4、p-4...
4、删除元素:根据实现方式删除元素
5、Java集合API中的散列表
- Hashtable
- HashSet
- HashMap
- IdentityHashMap
- WeakHashMap
- LinkedHashSet与LinkedHashMap等