首先介绍一下哈希技术,哈希技术是在记录的存储位置的记录的key之间建立一个确定的映射f(),使得每个key对应一个存储位置f(key)。若查找集合中存在这个记录,则必定在f(key)位置上。哈希技术既是一种存储方法,也是一种查找方法。
六种哈希函数f(key)的构造方法:
1、直接定址法
哈希地址:f(key)=a*key+b(a,b为常数)
这种方法的优点是:简单、均匀,不会产生冲突。但是需要预先知道key的分布情况,适合查找表较小并且连续的情况。
2、数字分析法
比如我们的手机号码“136xxxx5889”,其中前三位是接入号,一般对应不同运营公司的子品牌,比如130是联通,159是移动等等,中间四位表示归属地。最后四位才是用户号。
若我们现在要存储某家公司员工登记表,如果手机号码作为key,那么极有可能前7位是相同的,所以我们选择后四位作为f(key)就是不错的选择。
适合处理关键字位数较大的方法
3、平方取中法
顾名思义,比如key是1234,那么它的平方就是1522756,再抽取中间的三位就是227作为f(key)。
适合不知道关键字的分布,而位数又不是很大的情况。
4、折叠法
折叠法是将key从左到右分割成位数相等的几个部分(最后一部分位数不够可以短些),然后将这几部分叠加求和,并按哈希表的表长,取后几位作为f(key)。比如我们的key是9876543210,哈希表的表长为3位,我们将key分为4组,987|654|321|0,然后将他们叠加987+654+321+0=1962,再取后三位即f(key)=962.
适合事先不知道关键字的分布,关键字位数较多的情况。
5、除留余数法
哈希地址:f(key)=key mod p (p<=m)m为哈希表长。这种方法是最常用的哈希函数构造方法,下面的代码中也使用了这种方法。
6、随机数法
哈希地址:f(key)=random(key)。这里random是随机函数,当key长度不等时,采用这种方法比较合适。
哈希函数冲突的两种解决方法
哈希函数冲突的情况为key1!=key2但是f(key1)=f(key2),即发生冲突
1、开放定址法
开放定址法就是一旦发生了冲突,就去寻找下一个空的哈希地址,只要哈希表足够大,空的哈希地址总是能够找到,然后将记录插入。这种方法是最常用的冲突解决方法。下面的代码中也使用了这种方法。
2、链地址法
将所有相同关键字的记录存储在一个单链表中,称这种表为同义词子表,在散列表中只存储所有同义词子表的头指针。
3、再散列函数法
事先多准备几个散列函数
示例代码
package HashSearch;
import java.util.Scanner;
public class HashSearch {
/**
* 哈希查找算法
*/
//初始化哈希表
static int hashLength=7;
static int[] hashTable=new int[hashLength];
//原始数据
static int[] list=new int[] {13,29,27,28,26,30,38};
public static void main(String[] args) {
System.out.println("*****************哈希查找*****************");
//创建哈希表
for(int i=0;i<list.length;i++) {
insert(hashTable,list[i]);
}
System.out.println("展示哈希表中的数据: "+display(hashTable));
while(true) {
//哈希表查找
System.out.print("请输入要查找的数据:");
int data=new Scanner(System.in).nextInt();
int result=search(hashTable,data);
if(result==-1) {
System.out.println("对不起,没有找到!");
}else {
System.out.println("数据的位置是:"+result);
}
}
}
/**
* 哈希表插入
* @param hashTable
* @param data
*/
public static void insert(int[] hashTable,int data) {
//哈希函数,除留余数法
int hashAddress=hash(hashTable,data);
//如果不为0,则说明冲突
while(hashTable[hashAddress]!=0) {
//利用开放定址法解决冲突
hashAddress=(++hashAddress)%hashTable.length;
}
//将待插入值存入字典中
hashTable[hashAddress]=data;
}
/**
* 方法:哈希表查找
* @param hashTable
* @param data
* @return
*/
public static int search(int[] hashTable,int data) {
//哈希函数,除留余数法
int hashAddress=hash(hashTable, data);
while(hashTable[hashAddress]!=data) {
//利用开放定址法解决冲突
hashAddress=(++hashAddress)%hashTable.length;
//查找到开放单元 或者循环到原点,表示查找失败
if(hashTable[hashAddress]==0 || hashAddress==hash(hashTable, data)) {
return -1;
}
}
//查找成功,返回下标
return hashAddress;
}
/*
* 构建哈希函数(除留余数法)
*/
public static int hash(int[] hashTable,int data) {
return data%hashTable.length;
}
/**
* 展示哈希表
* @param hashTable
*/
public static String display(int[] hashTable) {
StringBuffer stringBuffer=new StringBuffer();
for(int i:hashTable) {
stringBuffer.append(i+" ");
}
return String.valueOf(stringBuffer);
}
}