`
xichao1929
  • 浏览: 40233 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

自己写的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
分享到:
评论

相关推荐

    利用Hash技术统计C源程序中关键字的频度

    扫描一个C源程序,用Hash表存储该程序中出现的关键字,并统计该程序中的关键字出现的度。用线性探测法解决Hash冲突。设Hash函数为:Hash(Key)=[(Key的首字母序号)*100+(Key的尾字母序号)] Mod 41。关键字39个,参考...

    Go-fasthashgo写的一个hash算法比标准hash算法的速度更快占用内存更低

    同时,查看示例代码或测试用例可以帮助你更好地理解如何在自己的项目中使用`fasthash`。 总的来说,`fasthash`是一个针对Go语言的高性能哈希库,它为开发者提供了在速度和内存效率之间取得平衡的选项。如果你的项目...

    GEOHASH Javascript的实现

    2. **二进制编码到`GEOHASH`字符串**:将二进制编码转化为字符串,通常使用Base32编码,因为Base32字母表只包含大小写字母和数字,没有可能引起混淆的字符。每5位二进制码对应一个Base32字符,因此一个32位的二进制...

    uthash源码

    UTHash 是一个开源的、轻量级的哈希表实现,用于C语言。它提供了一种在C数据结构中高效地存储和查找键值对的方法...通过对源码的深入学习,开发者可以更好地理解哈希表的工作原理,并能根据自己的需求进行定制和扩展。

    文章及子程序_Hashin准则_vumat_

    在复合材料领域,Hashin准则和VUMAT(User-Defined Material subroutine for the VASP code,VASP程序的用户自定义材料子程序)是两个重要的概念。本文将深入探讨这两个主题,以及它们如何应用于损伤计算和刚度退化...

    Hashin失效准则VUMAT源代码.rar_Hashin准则_VUMAT失效_hashin_vumat_断裂 vumat

    适用于Hashin失效准则,计算材料断裂。

    用于内存数据库的Hash索引的设计与实现

    Hash索引的设计和实现简单,通常比其他类型的索引(如B树索引)更节省空间,但其缺点在于不支持范围查询和排序操作,因为哈希函数可能导致键值的顺序不可预测。 在内存数据库中设计和实现Hash索引,需要考虑到内存...

    哈希表 哈希 (C++编写)

    哈希表,又称散列表,是一种数据结构,它实现了关联数组的概念,允许我们将键(Key)映射到相应的值(Value)。在哈希表中,通过一个特定的函数——哈希函数,我们可以快速地查找、插入和删除元素,通常的时间复杂度...

    linux c 写的hash算法的编程

    在Linux环境中,C语言是一种常用的系统...例如,在数据库中,哈希表可以实现快速的查找和插入操作;在密码学中,哈希函数用于加密和验证数据的完整性。因此,理解并掌握哈希算法对于任何IT从业者来说都是非常有价值的。

    Hash函数的设计优化

    Hash 函数的评价标准应为随机性,即对任意一组标本,进入 Hash 表每一个单元的概率的平均程度,因为这个概率越平均,数据在表中的分布就越平均,表的空间利用率就越高。 常用的 Hash 函数有三种:直接取余法、乘积...

    hash函数 c程序

    该函数可以在输入Hash_QueryParameters.txt文件下,每行为一个数据(可以随便写)输入哈希表中,可以查找,删除,插入等操作

    前端开源库-hash-stream

    `hash-stream`属于Transform Stream,它可以同时作为可读和可写的流,允许在数据流转过程中进行计算。 三、使用`hash-stream` 1. 安装:首先,你需要通过npm安装`hash-stream`库: ``` npm install hash-stream ...

    文件hash算法,对大文件进行hash计算

    常见的文件哈希算法包括MD5(Message-Digest Algorithm 5)、SHA-1(Secure Hash Algorithm 1)、SHA-256以及SHA-3系列。MD5曾经广泛使用,但由于其安全性较低,容易产生碰撞(即两个不同的文件可能生成相同的哈希值...

    RocksDB_rocksdb_快速hash存储_

    在"RocksDB 快速hash存储"这个主题下,我们将深入探讨RocksDB如何实现高效的哈希存储及其背后的优化策略。 ### 1. 哈希表基础 哈希表是一种常用的数据结构,通过哈希函数将键(Key)映射到数组的特定位置来实现...

    LM HASH 计算工具

    每一步都有,NT以后可能会写,LM的计算中的DES计算没写好,需要手工计算利用里面的工具,都怪自己懒。为了考试的时候不想手算这东西才做的,以后有时间会逐步的调整,需要源码的联系QQ:3281540895

    NoSQL数据库之Redis数据库管理三(string类型和hash类型)

    本篇文章将主要聚焦于Redis中的两种基础数据类型——String和Hash,以及它们在数据库管理中的应用。 首先,让我们深入了解String类型。在Redis中,String是最基本的数据类型,可以存储字符串、整数或浮点数。例如,...

    Hash值计算小工具

    《Hash值计算小工具详解与应用》 在信息技术领域,数据的完整性和一致性至关重要,而Hash(哈希)函数在其中扮演了关键角色。本文将详细介绍Hash值计算小工具的功能、工作原理及其在实际应用中的价值,特别是如何...

    MD5Hash.rar

    在信息安全领域,MD5 Hash是一种被广泛认可和应用的散列函数,其全称为Message-Digest Algorithm 5,是一种能够产生128位散列值的加密算法。该算法以Ronald Rivest的名字命名,他在1991年设计了这一算法,主要用途是...

    哈希表------链表

    哈希表--链表 哈希表--链表 哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表

Global site tag (gtag.js) - Google Analytics