数据结构之哈希表

数据结构之哈希表
1.哈希表简介
2.冲突
3.重载因子
4.一些常用的Hash算法
1.先来看看哈希表在百度百科的解释,哈希表是根据关键码值而直接经行访问的数据结构。也就是说,他通过把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做散列函数。
其中有些关键字:关键值...数据结构....映射.....查找速度
简单的说,哈希表是一种比普通查找方法查找起来更快的数据结构,他是一种数据结构,他的存储方法是通过一组数据(key)的映射来决定的,所以只要能得到这组数据,那么就能根据自己设定的映射方式(一般我们叫做哈希函数)来直接算出该数据存储的位置,以达到快速查找的目的。利用这种存储结构,可以更快的查找到我们要查找的数据。
1.冲突,由于存储地址是有限的和固定的,那么在我们的映射方式(哈希函数)对于多个key值经行映射时,就会可能有多个key值同时算出来一个地址,这种现象叫做冲突,解决冲突的方式很多,简要介绍几种方式
1.再散列法,就是在发生冲突时,将容器改变,重新经行哈希,直到不产生冲突为止。
2.链地址法,在产生冲突时,在原来的地址上建立一个链表。
3.建立公共溢出区,把产生冲突的数据,全部储存到一块区域里去。
这里主要讲链地址法。
2.重载因子,在链地址法处理冲突时,其实是用空间换取时间,数组下面建立链表,这样既拥有了数组的方便寻址,又有链表的方便增删。冲突的频率过于频繁,而当冲突同时发生在同意区域时,其实就形成了一支链表,这样就根本不能达到减少查找时间的效果,所以我们要控制某一支链表的长度,当某一支链表超过某个比例时,我们应当做某些处理,以减少查找时间,增加查找效率。这个比例我们乘坐重载因子,当某一支链表的长度比上整个数组的的长度大于重载因子时,将数组扩容,所有元素重新排列,称之为再哈希。这个重载因子决定了查找的效率,太大查找效率会偏低,太小会导致再哈希过于频繁,浪费时间。据科学家计算过,当重载因子为0.75时,算法效率最高。
3.哈希算法
1.直接寻址法 根据key值用某个线性函数获取地址
2.数字分析法 利用重复数字较少的部分构成散列地址,减少冲突
3.平法取中法 关键字平方后去中间几位作为散列地址
4.除留取余法 取某个值p,用key值除p得到的值取余数作为散列地址
5.随机数法 取一组随机数作为散列地址
这些方法是平时一般比较常用的方法,说到这里的话,我想大家都已经懂了,利用key值映射得到地址,然后去相应的地址里找。就像是一道脑筋急转弯,我告诉你老师所在的楼层是key的三倍加一,然后告诉你key是2,你能知道老师在那个楼层么,2是已经给出的key,3x+1是哈希函数,利用函数寻找地址就是这个意思。ok,献上一段链地址法的代码,哈希函数是平方取余
public class Hash {
private Maping[] array ; //存储数组
private double factor = 0.7; //装载因子
private int count = 10; //默认装载个数
private int enough = 6; //超出后的添加数

/**
* 使用默认构造器,构造一个Hash
*/
public Hash(){
array = new Maping[count];
}
/**
* 使用自定义的构造器,构造Hash
* @param count 自定义的初始装载个数
* @param factor 自定义的装载因子
*/
public Hash(int count,float factor){
this.count = count;
this.factor = factor ;
array = new Maping[count];
}
/**
* 跟据数据的key值查找
* @param key 映射的key值
* @return 返回元素的value值,如果不存在,返回null
*/
public Object searchValue(int key){
//计算出该元素所存在的数组索引
int code1 = getHashCode(key);
//根据索引所在的数组元素,往链表之下寻找
Maping temp = array[code1];
while(temp != null){
//如果找到元素,返回value值
if(temp.getData() == key){
return temp.getValue();
}//如果没找到,继续往下找
else{
temp = temp.getNext();
}
}
return null;
}
/**
* 添加一个数据进入Hash,该Hash允许相同的value值的存在,未处理相同key值的存在(原则上讲不允许相同的key值)
* @param m 要添加的元素
*/
public void add(Maping m){
int xCount = 1; //记录该条链上所拥有的元素个数
//利用自己写的Hash函数,获取在数组中应存放的位置
int code1 = getHashCode(m.getData());
//如过为空,将他放在里边
if(array[code1]==null){
array[code1] = m;
}//如果不为空
else{
//一直找到链表的末尾,将新添加的元素放在队尾
Maping temp = array[code1];
xCount++;
while(temp.getNext() != null){
xCount++;
temp = temp.getNext();
}//添加进入队尾
temp.setNext(m);
m.setNext(null);
}

//如果链表长度超过了比例,也就是超过重载因子的限制,那么就重新排列reHash
if((double)xCount/(double)count>factor){
reHash();
}
}
/**
* 利用自己写的Hash函数,获取应存放的位置
* 其实这只是一个简单的映射,这种映射可以随自己的意愿更改
* @param data 函数的利用值
* @return 返回得到的Code值
*/
public int getHashCode(int data){
//平方取余法
int value = (data*data)%count;
return value;
}
/**
* 由于装载数超过装载因子数目,重新把元素提出,将数组扩容,再次Hash
*/
public void reHash(){
//将已有的Hash中的数据全部存储起来
List<Maping> list = new ArrayList<Maping>();
for(int i=0;i<array.length;i++){
Maping temp = array[i];
while(temp != null){
Maping temp1 = temp.getNext();
//把得到的节点隔离出来
temp.setNext(null);
list.add(temp);
//赋值,还原指针
temp = temp1;
}
}
//重置Hash数组,并改变容量
count = count + enough;
array = new Maping[count];
//重新添加
for(int i=0;i<list.size();i++){
Maping m = list.get(i);
add(m);
}
}
/**
* 打印当前的Hash表(测试用)
*/
public void print(){
for(int i=0;i<array.length;i++){
System.out.println(i+"---->");
if(array[i]==null){
continue;
}
else {
Maping temp = new Maping(0, "temp");
temp = array[i];
while(temp != null){
System.out.println(temp.getData()+" "+temp.getValue());
temp = temp.getNext();
}
}
System.out.println();
}
}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值