哈希法的思想实际上就是建立一种映射,一种建立在元素关键字k与元素存储位置p之间的映射H,使得p = H(k) , 此函数称之为哈希函数。
构造哈希函数方法有很多:
数字分析法:
从关键字中选出分布较为均匀的若干位,构成哈希地址,但是此种情况一般适用于能预先知道关键字集合,并且每个关键字的位数比哈希表的地址码位数多时
平方取中法:
以关键字的平方值的中间几位作为存储地址,这适用于关键字中的每一位都有某些数字重复出现频度很高的现象
分段叠加法:
将关键字分为位数相等的及部分,然后将这几部分相加,再舍弃最高进位后的结果当作哈希地址。这适用于关键字的数字位数特别多的情况
除留余数法:
假设哈希表长为m,p为不大于m的素数,则哈希函数为:H(k) = k %p。
伪随机数法:
采用一个伪随机数作为哈希函数:H(K) = random(K);这适用于关键字长度不等,分布不均的情况。
在构造哈希函数的过程中,难免会发生以下情况:
有待散列元素(18,75,60,42,54) 取p=7,用除留余数法进行构造时: H(18) = 18 %7 = 4 ; H(75)= 75%7 = 5 ; H(60) = 60%7 = 4 ......
这样,元素18和60的哈希地址就产生冲突了,为了解决这个问题,通常有三种办法:
开放定址法:
即当关键字key的哈希地址p=H(key)出现冲突时,以p为基础,产生另一个哈希地址p1 ,如果p1仍然冲突,再以p为基础,产生另一个哈希地址p2……直到找出一个不冲突的哈希地址pi,将相应元素存入其中。
再哈希法:
构造若干个哈希函数,当发生冲突时,根据另一个哈希函数计算下一个哈希地址,直到冲突不再发生。即:Hi=Rhi(key) i=1,2,……k,其中:Rhi——不同的哈希函数,这种方法不易产生聚集,但增加了计算时间。
链地址法:
将所有哈希地址相同的关键字都存储在同一链表中。
以下是运用除留余数法和开放地址法进行哈希查找的JAVA代码:
import java.util.Arrays;
import java.util.Random;
public class HashSearch {
static int m; // 哈希表长
static int hashTable[]; // 哈希表
public static void main(String[] args) {
int a[] = new int[100000];
for (int i = 0; i < a.length; i++) {
a[i] = i * 2;
}// 初始化数组,数组中全是偶数
int key = new Random(System.currentTimeMillis()).nextInt(200000);
// 随机产生要查找的关键字
for (int i = a.length;; i++) {
if (isSushu(i)) {
m = i;
break;
}
}// 根据数组大小确定哈希表长
hashTable = new int[m];
Arrays.fill(hashTable, -1);// 将哈希表全部初始化为-1
System.out.println(key + " " + hashSearch(a, key));
}
public static boolean hashSearch(int a[], int key) {// 哈希查找
ConstructHash(a);// 根据数组构造出哈希表
int position = key % m;
if (hashTable[position] == -1) {
return false;
} else if (hashTable[position] == key) {
return true;
} else {// 用线性探测再散列解决冲突
for (int i = 1; i < m; i++) {
int pi = (position + i) % m;
if (hashTable[pi] == -1) {
return false;
} else if (hashTable[pi] == key) {
return true;
}
}
return false;
}
}
public static void ConstructHash(int a[]) {// 构造哈希表
for (int i = 0; i < a.length; i++) {
int pi = Hash(a[i]);
if (hashTable[pi] == -1) {
hashTable[pi] = a[i];
} else {// 用线性探测再散列解决冲突
for (int j = 1; j < m; j++) {
int position = (pi + j) % m;
if (hashTable[position] == -1) {
hashTable[position] = a[i];
j = m;//中断内循环
}
}
}
}
}
public static int Hash(int n) {// 除留余数法构造哈希函数
return n % m;
}
public static boolean isSushu(int i) {// 将哈希表长定为素数
int k = (int) Math.sqrt((double) i);
for (int j = 2; i <= k; j++) {
if (i % j == 0) {
return false;
}
}
return true;
}
}