`
文昌平蓝杰
  • 浏览: 54537 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

数据结构之哈希表

阅读更多
数据结构之哈希表
      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();
		}
	}
}
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics