散列技术是在记录的储存位置和它的关键字建立一种确定的对应关系f,使得每个关键字key对应一个储存位置(key)。查找时,根据这个确定的对应关系找到给定值key对应的映射f(key),若查找集合中存在的记录,则必在f(key) 的位置上。
这里我们把这种对应关系f称为散列函数,又称为哈希函数。按照这个意思,采用散列技术将记录存储在一块连续的存储空间中,这块连续的存储空间称为散列表或者哈希表。
哈希表查找步骤:
1.在存储时,通过散列函数计算记录的散列地址,并按此地址存储该记录。
2.当查找记录时,我们通过同样的散列函数计算记录的散列地址,按此散列地址访问该记录。很简单,在哪里存,就在哪里找。
所以说,散列技术既是一种存储方法,也是一种查找方法。它与线性表,图等结构不同的是,前面几种元素之间存在某种逻辑关系,可以用线性图示表示出来,而散列技术的记录之间不存在什么逻辑关系,它只与关键字有关。因此,散列技术主要是面向查找的存储结构。
散列技术最合适的求解问题是查找与给定值相等的记录。它简化了过程比较,效率就会大大提高。但是,散列技术不具备很多常规数据结构的能力。
- 同样的关键字有很多的记录存在。如:公司找人你要按照男女去找那就难受了啊,一个女可能对应很多人的,但是按照公司的工作号去找那就倍儿快。
- 不适合范围查找,你不能找一个公司中30-40岁之间的员工,也不能找年龄最大或者最小的员工,甚至你无法对哈希表排序。
散列函数的构造方法:
两个原则:
- 计算简单。
- 散列地址分布均匀,这样可以保证空间的利用率,并减少处理冲突的时间。
构造方法:
- 直接地址法:去关键字的某个线性函数值为散列地址,f (key) = a * key +? b.这样的散列函数优点就是简单均匀,也不会产生冲突,但问题是这需要事先知道关键字的分布情况,适合查找表较小且连续的情况。由于这样的限制,此方法虽然简单,但现实中并不常用。
- 数字分析法:比如手机号,182****8213你就知道,这是移动的手机号,并且能锁定那个人。截取有效关键字,这是散列技术常用到的手段。数字分析法通常适合处理关键字比较大的情况,如果事先知道关键字的分布,就可以考虑这个方法。
- 平方取中法:比如数字1234,平方1522756,在抽取中间三位227,用作散列地址。它适用于不知道关键字的分布,而位数又不是很大的情况。
- 折叠法:将关键字从左到右分割成相等的几部分(最后一位不够就让它短吧),然后把这几部分叠加求和,并按照散列表长,取最后几位作为列数地址。如:关键字9876543210,散列表长为3位,我们将它分成四组:987 654 321 0 然后叠加求和 987 + 654 + 321 + 0 = 1962,再取后三位得散列地址,962。有时这样还不能保证分布均匀,不妨从一端向另一端折叠后对齐相加。如把987 和 654 反转,再与 654 和 0 相加 ,1566,所以散列地址为556。折叠法实现不知道关键字的分布,适合关键字位数较多的情况。
处理散列冲突的方法:
- 开放定址法:发生冲突,就去寻找下一个空的散列地址只要散列表足够大,空的散列地址总能够找到,并将记录存入。解决冲突的开放定址法有:线性探测法,二次探测法。本来不是同一个词儿取要争夺同一个空间的现象叫堆积。
- 在散列函数法:。我们可以把我们前面说的折叠法,平方取中法都用上,放生冲突我们就换一个函数。
- 链地址法:将所有的关键字为同义词的记录存储在一个单链表中。这样已经不存在什么冲突地址,无论多少冲突,我们增加结点就可以解决。
Java代码实现:
// 哈希表
public DataArray(int[] paraKeyArray, String[] paraContentArray, int paraLength) {
// 初始化
length = paraLength;
data = new DataNode[length];
for (int i = 0; i < length; i++) {
data[i] = null;
} // Of for i
// 第二步
int tempPosition;
for (int i = 0; i < paraContentArray.length; i++) {
// 构建哈希表
tempPosition = paraKeyArray[i] % paraLength;
// 寻找一个空位
while (data[tempPosition] != null) {
tempPosition = (tempPosition + 1) % paraLength;
System.out.println("Collision,move forward for key " + paraKeyArray[i]);
} // Of while
data[tempPosition] = new DataNode(paraKeyArray[i], paraContentArray[i]);
} // Of for i
} // Of the constructor
// 哈希查找
public String hashSearch(int paraKey) {
int tempPosition = paraKey % length;
while (data[tempPosition]!=null) {
if (data[tempPosition].key == paraKey) { // 判断关键字是否相等。
return data[tempPosition].content; // 返回内容
} // Of if
System.out.println("Not this one for "+paraKey);
tempPosition = (tempPosition+1)%length;
} // Of while
return "null";
} // Of hashSearch
完整代码
|