自己写的hash表_JAVA_编程开发_程序员俱乐部

中国优秀的程序员网站程序员频道CXYCLUB技术地图
热搜:
更多>>
 
您所在的位置: 程序员俱乐部 > 编程开发 > JAVA > 自己写的hash表

自己写的hash表

 2011/11/24 10:44:24  xichao1929  http://xichao1929.iteye.com  我要评论(0)
  • 摘要:Hash表的基本思想是:将一组数存放到hash表中,我们使用一个函数,叫做hash函数,它的自变量是这些数据,它的函数值就是这个数据在hash表中的位置。因此这个hash函数又叫做hash地址。使用这样的函数,我们必然会设想到这样的一个问题,就是函数值相同。也就是两个不同的数据具有相同的位置,这显然是有问题的。这对于存储数据来说是不允许的。我们把上面的的这种情况叫做hash冲突,而出现这种情况的数据对象(hash函数的自变量)我们把它们称之为同义词,那么这种冲突又叫做同义词冲突
  • 标签:has Hash 自己

Hash表的基本思想是:
?? 将一组数存放到hash表中,我们使用一个函数,叫做hash函数,它的自变量是这些数据,它的函数值就是这个数据在hash表中的位置。因此这个hash函数又叫做hash地址。

?? 使用这样的函数,我们必然会设想到这样的一个问题,就是函数值相同。也就是两个不同的数据具有相同的位置,这显然是有问题的。这对于存储数据来说是不允许的。我们把上面的的这种情况叫做hash冲突,而出现这种情况的数据对象(hash函数的自变量)我们把它们称之为同义词,那么这种冲突又叫做同义词冲突。这个问题出现的原因是在这个hash函数身上。但是在实际情况下这个问题的还有别的原因。我们先这样理解:我们要存放数据的空间是不够的,那么这样以来理论上必然有些数据的是要放在一起的。当然这是一种错的理解。事实上空间肯定是够的。不过我们要知道这样的一件事:数据的取值的区间是一定比hash函数的变化范围大的。那么自然我们是要开辟新的空间来保证数据的存储。可能说的有点多,总结一下两个概念:1hash函数,2hash冲突。胡哥说,我们对一个新的概念真正理解的标准是离开术语,最好是用比喻的方式谈自己的观点。显然根据这个标准我是没有理解这两个概念的,但是我这样认为,胡哥的标准是一种高的标准。那么就会存在中间的一个标准:就是把这些知识在自己的脑海沉淀一下,然后能有自己的理解,尽管和书上或者和别人讲的有些雷同,这就可以表示我们达到了中间的那个标准(虽然是雷同,但毕竟是自己虚构的吗呵呵)。有些问题,我们一开始不可能马上就达到胡哥所倡导的标准。不过我们需要先达到某个标准。)

对于这个hash表?呢,我只是根据我的理解,写了一下。其中的很多东西,是没有用到的,比如hash函数的创建方法,有好多种,显然我只用了一种。不过可从感性上先了解了解,别的方法的思想也是不错的,以后用到的话也不会陌生。

hash函数的构造方法

1.是直接定址法:

它的计算法hash地址的方法就是,对关键字做一个简单的加法运算。公式是:hk=k+c

这个方法的应用环境就是关键字的分布比较连续的时,因为不连续的话,就会造成资源的浪费。而且这种方法,是不会产生hash冲突的。

2.除留余数法(我用的就是这种方法):

???公式是:?h(k)=k??mod??p

?其中p最好是素数,我在使用的时候:p是小于hash表长度的最大的素数。这种方法自然产生冲突,我解决冲突的方法就是所谓的拉链法(就是出现冲突以后,就在这个位置上在申请空间,然后放数据),不过在我的代码中解决冲突的方法不太明显,好像就是在潜意识中解决的。

3数字分析法

??简单的说,就是对我们要存方法的数据,单纯考察他们的数据的规律,来得到hash函数,这个东西会在什么地方用到,一时想不起来,好像没什么地方(除了数学中)在实际生活中不太好找这样有规律的数据。这东西可能科学家经常用,在实际生活中估计用到的不多。

????

处理hash函数的方法

?就是当冲突产生的时候,为hash地址产生冲突的关键字,再提供新的位置(如果还是冲突的就在提供新的位置)。

??? 开放定址法:当产生冲突时,就根据这个冲突的hash地址的值,计算新的hash地址(直到不出现冲突)。会有这样的情形出现:就是本来不是同义词的关键字,也会出现冲突,术语叫做非关键字冲突。

?? 拉链法:就是当冲突出现的时候,把这个数据加到以这个地址为头节点的链表的后面。

?

以上是自己所知道一些总结。下面简单的说说自己的代码。

?

我自己构造了一个链表,然后又以链表的类型够造了数组。这样呢,处理hash冲突的方法自然就是所谓的拉链法了。

?然后我的装载因子也大于1了。实际是这样的情况:当某一个链表的长度,超过了数组长度开平方后的值的话,就要rehash。(其中数组长度开平方就是我的装载因子)

还有就是我想说一下,一开始我的这个hash只是int型,但是大圣一说最好是Object,然后就想办法改了,显然最好是用Object的地址,但这个行不通,因为字符串的toString()方法不配合,最后用的是hashCode(),这个方法可能会返回负值,所以也要稍微处理一下。

?

下面是代码,其中的解释是当时自己写代码时候的想法,我想这个解释最好是留着。

?
package myhash;

/**
 * 这是我定义的链表的类 我应该好好想想
 * 
 * @author 田西超
 * 
 */
public class TxcNode {

	// 里面的一些基本的属性

	public TxcNode next = null;
	public TxcNode previce; // 取得前面的一个节点,在本程序中只有tail使用这个属性
	public Object data;// 表示这个结点的值

	public TxcNode() {
	}

	public TxcNode(Object data) {
		this.data = data;
	}

}
?
package myhash;

public class TxcLink {

	// 这里边的属性应该是长度,头结点 尾结点之类的东西
	public TxcNode head;
	public TxcNode tail;
	public int length = 0;

	// 每一个链表 都应该有结点和尾节点
	public TxcLink() {// 构造函数中就要有这个 头和尾结点
		head = new TxcNode();
		tail = new TxcNode();
	}

	// 链表中应该有添加一个结点的方法
	public void add(Object o) {
		TxcNode tn = new TxcNode(o);
		if (length == 0) {
			head.next = tn;
			tn.next = tail;
			tail.previce = tn;
			length++;
		} else {
			// 取得当前的结点
			TxcNode tem = tail.previce;
			tem.next = tn;
			tn.next = tail;
			tail.previce = tn;
			length++;
		}

	}

	

}
?
package myhash;

public class TxcHash {
	private int modelLen;// 这个属性是那个 hash我在写hash函数的 那个分母 (这是个素数)
	public TxcLink[] txcArray;
	private int loadFactor;// 这个是装载因子
	private int endlength = 0;

	// 我写了两个构造函数
	public TxcHash() {
		// 首先创建一个数组
		txcArray = new TxcLink[100]; // 这里边放的是head
		// 这个数组里面是 存放地址还是 数据呢,我看最好是地址。也就是一个链表
		modelLen = 97;// modelLen一般小于TxcNode的长度
		loadFactor = 10;
		endlength = 100;
	}

	public TxcHash(int initLength, int modelLen) {
		txcArray = new TxcLink[initLength];
		this.modelLen = modelLen;
		loadFactor = (int) Math.sqrt(initLength);
	}

	// 这里边也要有添加元素的方法 其实就是构造hash的方法 我用的是 除模取余的方法
	// 我是定义了这个 TxcNode的
	public int hashFun(Object o) {
		int i = Math.abs(o.hashCode());
		int index = i % modelLen;
		return index;
	}

	/**
	 * 现在是处理hash冲突 其实我一开想好了或者说是定义了 处理hash冲突的方法 在这个地方就不明显了 首先有这个 数值计算出
	 * 位置,然后在这个位置上我们创建一个 链表的对象 放到这个位置上, 然后用这个链表的来添加数据 由hash 函数计算出这个 这个对象的位置,
	 * 
	 * @param i
	 */
	public void hashAdd(Object i) {
		int index = hashFun(i);// 求得i元素的位置 如果这个位置不冲突的话 就要申请一个新的结点, 冲
		// 突的话 就不需要了
		if (txcArray[index] != null) {// 冲突不发生时 的情况
			if (txcArray[index].length != 0)
				txcArray[index].add(i);// 。
		} else {
			TxcLink tl = new TxcLink();
			tl.add(i);//
			txcArray[index] = tl;// 调用添加数据的方法
		}
		if (txcArray[index].length > loadFactor) {
			System.out.println(loadFactor);
			rehash();

		}

	}

	/**
	 * 这个rehash函数
	 */
	public void rehash() {
		// 下面开始 rehash 这个是非常重要的环节
		int l = txcArray.length;
		TxcLink newtxcArray[] = new TxcLink[2 * l];
		endlength = 2 * l;
		loadFactor = (int) Math.sqrt(2 * l);// 修改装载因子
		modelLen = prime(2 * l);// 这个是 模的修改
		// 以上是一些属性的修改
		// 下面是 数据的 重放
		for (int j = 0; j < l; j++) {
			if (txcArray[j] != null) {
				int ll = txcArray[j].length;// 连表的长度
				if (ll > 0) {
					TxcNode tn = txcArray[j].head.next;
					while (ll > 0) {
						int newindex = hashFun(tn.data);// 重新分配位置

						if (newtxcArray[newindex] != null) {
							if (newtxcArray[newindex].length != 0)
								newtxcArray[newindex].add(tn.data);// 。
						} else {
							TxcLink tl = new TxcLink();
							tl.add(tn.data);
							newtxcArray[newindex] = tl;
						}

						tn = tn.next;
						if (newtxcArray[newindex].length > modelLen) {
							rehash();
						}
						ll--;
					}
				}
			}

		}

		txcArray = newtxcArray;

	}

	/**
	 * 这个打印hash中的所有的数据
	 */
	public void printall() {
		for (int i = 0; i < endlength; i++) {
			if (txcArray[i] != null) {
				TxcLink tk = txcArray[i];
				TxcNode tem = tk.head.next;
				for (int j = 0; j < tk.length; j++) {
					Object d = tem.data;
					System.out.print("这些数据是:" + d);
					tem = tem.next;
				}

			}

		}

	}

	/**
	 * 查找数据的方法
	 * 
	 * @param d
	 * @return
	 */
	public boolean search(Object d) {
		System.out.println("这个数是:" + d);

		int index = hashFun(d);
		TxcLink tk = txcArray[index];
		if (tk == null) {
			return false;
		} else {
			TxcNode tem = tk.head.next;
			for (int i = 0; i < tk.length; i++) {
				if (tem.data.equals(d)) {
					System.out.println("找到了是:" + tem.data);
					return true;
				} else {
					tem = tem.next;
				}
			}
		}
		return false;

	}

	/**
	 * 这个是求得不超过某个数的最大的素数
	 * 
	 * @param l
	 * @return
	 */
	private int prime(int l) {
		int modelLen = 0;
		for (int i = l - 1; i >= 0; i--) {
			if (isPrimeNumber(i)) {
				modelLen = i;
				return modelLen;
			}
		}
		return modelLen;
	}

	/**
	 * 判断某个数是否为素数
	 * 
	 * @param i
	 * @return
	 */
	private boolean isPrimeNumber(int i) {
		if (i >= 2) {
			for (int j = 2; j <= Math.sqrt(i); j++) {
				if (i % j == 0)
					return false;
			}
			return true;
		}
		return false;
	}

}
?
package myhash;

public class HashTest {

	public static void main(String args[]) {
		// java.util.Random ra = new java.util.Random();

		TxcHash th = new TxcHash();
		Long beginTime = System.currentTimeMillis();// 记录 开始时间

		for (int j = 0; j < 1000000; j++) {

			th.hashAdd("txc" + j);
		}
		th.hashAdd("jjs");
		Long endTime = System.currentTimeMillis();// 记录结束时间
		System.out.println("该程序总用时为:" + (endTime - beginTime));
		Long beginTime2 = System.currentTimeMillis();
		boolean b = th.search("txc" + 3532);

		// th.printall();
		Long endTime2 = System.currentTimeMillis();
		System.out.println("查找的时间:" + (endTime2 - beginTime2) + "是否找到:" + b);

	}

}
?该程序总用时为:2374
这个数是:txc3532
找到了是:txc3532
查找的时间:0是否找到:true
发表评论
用户名: 匿名