作为一个程序员或一个软件工程师,hash(散列)必然是熟解的。
对于百万级,甚至更多的数据的数据,我们如何实现存储并不太难,可我们要实现快速的增添查找删,显然普通的数组和链表是满足不了的。数组可以满足快速的查找,链表可以实现随意的删除,我们由此不难得到一种数据结构:数组挂链表的结构,称挂链式。
以一般的数据结构实现哈希过程,在实际中会遇到比较大的问题,我们在此选择使用挂链式,作为解决hash冲突的一种解决策略。当有不同的key在hash()后得到想的hashcode的时候,我们选择在哈希表结点上悬挂链表。而当我们的数据虽时间变化的时候,显然,冲突越来越严重,我们需要解决这个问题。在冲突达到一定程度时,我们队哈希表进行扩张,对原哈希表的数据进行rehash()。
不难想象到,rehash()是一个浪费时间与空间的过程,可这是没有办法的事,因为两全其美的事不存在,我们只能根据实际情况,取舍。
自定义哈希表的结构
①hash():哈希表实现快速增删查的根本,通过hash()对每一个key 得到唯一的一个索引,而这个索引就如身份证号,查找删除数据时只需知道与此数据相匹配的索引;
②解决冲突的方法:冲突指的是当不同的数据的key通过hash()得到相同的索引位置而提供的解决方法,此处此方法为挂链式;
③rehash():当哈希表中的数据达到一定的值时,成为阀值,因为此刻由于数据的增多,索引的速度必然受到影响,而解决的方法就是对数组拓展,重新的hash().
class HuHash{
private Node<Item> [] list=new Node<Item> [len];
public boolean put(K k,V v){
if(冲突严重){
rehash()
}
int index=hash(k)%len;
Node<Item> node=new Node<Item>(k,v);
if(list[index]==null){
}
}
public boolean get(K k){
}
public boolean hash(K k){
}
public boolean rehash(){
}
}
class Node<Item>{
private Node parent;
private Node next;
}
class Item{
}
性能对比(见附件):
代码实现如下:
package hash实现;
/**
* 哈希的实现类
*
* @author lenovo
*
*/
public class MyHash<K, V> {
private int len;
private Node[] list;
private int count; // 计数器
public MyHash(int len) {
this.len = len;
list = new Node[len];
count = 0;
}
/**
* 添加元素的方法
*
* @param key
* :键
* @param value
* :
* @return
*/
public void put(K key, V value) {
if (count >= 1.25 * len) { // 冲突的严重性
rehash();
}
int index = hash(key) % len;
Item<K, V> it = new Item(key, value);
Node node = new Node(it);
if (list[index] != null) { // 判断是否为空结点
// 添加到链表头
Node temp = list[index];
node.setNext(temp);
}
list[index] = node;
count++;
}
public V get(K k) {
int index = hash(k) % len;
Node temp = list[index];
while (temp != null) { // 判断此数组索引是否为空
if (temp.getItem().getK().equals(k))
return (V) temp.getItem().getV();
temp = temp.getNext();
}
return null;
}
/**
* 根据键移除hash表中的数据
*
* @param k
* @return 若存在则返回true
*/
public boolean remove(K k) {
if (count == 0) {
throw new RuntimeException("哈希表中无元素,删除操作拒绝!");
}
int index = hash(k) % len;
Node temp = list[index];
while (temp != null) { // 判断此数组索引是否为空
if (temp.getItem().getK().equals(k)) {
list[index] = temp.getNext();
count--;
return true;
}
temp = temp.getNext();
}
return false;
}
/**
* 得到哈希表中元素的个数
*
* @return
*/
public int size() {
return count;
}
/**
* SDBM散列方法
*
* @param k
* :键
* @return 哈希值
*/
private int hash(K k) {
String str = (String) k;
int hash = 0;
char[] chars = str.trim().toCharArray();
int num = 0;
while (num < chars.length) {
hash = (int) chars[num] + (hash << 6) + (hash << 16) - hash; // 进行移位运算
num++;
}
return (hash & 0x7FFFFFFF); // 按位与(hash和0x7ffffff转换为二进制)
}
/**
* 再散列方法
*/
private void rehash() {
len = len * 15; // 表长扩大的倍数
Node[] temp = new Node[len]; // 存放rehash后数据的数组
Node node;
int index;
for (int i = 0; i < list.length; i++) {
node = list[i];
while (node != null) {
index = hash((K) node.getItem().getK()) % len;
temp[index] = node;
node = node.getNext();
}
}
list = temp; // 复制数据
}
}
package hash实现;
/**
* 链表结点类,存放键值对
* @author lenovo
*
*/
public class Node {
private Node next;
private Item item;
public Node(Item item){
this.item=item;
next=null;
}
public Item getItem() {
return item;
}
public void setItem(Item item) {
this.item = item;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
}
package hash实现;
/**
* 键值对
* @author lenovo
*
*/
public class Item<K,V> {
private K k;
private V v;
public Item(K k,V v){
this.k=k;
this.v=v;
}
public K getK() {
return k;
}
public void setK(K k) {
this.k = k;
}
public V getV() {
return v;
}
public void setV(V v) {
this.v = v;
}
}
其他参考的hash函数:
/**
* 一位接一位的hash函数
*
* @param k
* @return
*/
private int oneByOneHash(K k) {
String str = (String) k;
int hash = 0;
for (int i = 0; i < str.length(); ++i) {
hash += str.charAt(i);
hash += (hash << 10);
hash ^= (hash >> 6);
}
hash += (hash << 3);
hash ^= (hash >> 11);
hash += (hash << 15);
return hash;
}
/**
* FNVHash 函数
*
* @param k
* @return
*/
private int FNVHash(K k) {
String str = (String) k;
final int p = 16777619;
int hash = (int) 2166136261L;
for (int i = 0; i < str.length(); i++)
hash = (hash ^ str.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
return hash;
}
分享到:
相关推荐
哈希表的建立和查找哈希表的建立和查找哈希表的建立和查找哈希表的建立和查找
Java哈希表性能优化
////采用除留余数法定义哈希表,哈希表长度为10,哈希函数为H(key)=key%13。产生冲突时采用线性探测法实现下面要求的功能。 ////(1)初始化哈希表,置空哈希表 ////(2)在哈希表中查找元素 ////(3)在哈希表中...
对一批关键字集合采用开放定址哈希表的存储结构来建立相应的哈希表和完成查找过程。 (1) 熟练掌握哈希表的构造方法 (2) 理解哈希表与其他结构表的实质性差别。
/为班级30个人的姓名设计一个哈希表,假设姓名用汉语拼音表示。要求用除留余数法 构造哈希函数,用线性探测再散列法处理冲突,平均查找长度的上限为2。 编写数据结构和算法来实现。要求:将哈希函数和处理冲突方法...
哈希表应用 设计哈希表实现图书查找系统,完成相应的建表和查表程序。从键盘输入各图书相关信息,分别以图书编号为关键字建立散列表。待填入哈希表的书号至少30个;构造合适的哈希函数。 (1)记录由外部输入。 (2...
哈希表设计程序设计+数据结构实验报告 1.1 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 zhuangshuangshuang)...
rustc 中使用的快速哈希算法。liballoc 中的 hashmap 默认使用 SipHash,它并没有我们想要的那么快。在编译器中,我们并不真正担心 DOS 尝试,因此我们使用快速非加密哈希。 这与 Firefox 使用的算法相同——它是一...
哈希表课程设计数据结构实验报告——哈希表设计 针对某个集体中的人名设计一个哈希表,使得平均查找长度不超过R,完成相应的建立和查表程序. 1.2 人名为汉语拼音形式,最长不超过18个字符(如:庄双双 ...
哈希表的概念作用及意义,哈希表的构造方法
哈希表的设计与实现课程设计 问题描述:针对某个单位电话号码簿,设计一个哈希表,并完成相应的建表和查表程序。 基本要求:设每个记录有下列数据项:电话号码、用户名、住址。从键盘输入各记录,以用户名为关键字...
哈希表的设计与实现——链地址法 问题描述: 设计哈希表实现电话号码查找系统。 基本要求: (1)设每个记录有下列数据项:电话号码、用户名、地址; (2)从文件中读取各记录,分别以电话号码和用户名为关键字建立...
哈希表--链表 哈希表--链表 哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表哈希表--链表 哈希表--链表
问题描述:针对某个单位电话号码簿,设计一个哈希表,并完成相应的建表和查表程序。 基本要求:设每个记录有下列数据项:电话号码、用户名、住址。从键盘输入各记录,以用户名为关键字建立哈希表,哈希函数用除留取...
用C语言实现的哈希表设计,内有姓名列表,只要输入姓名就可得到查到在哈希表中的相应位置
简单哈希表模型。可以助你更好的完成哈希表相关的编程。
假设人名为中国人姓名的汉语拼音形式。待填入哈希表的人名共有30个,取平均查找长度的上限为2。哈希函数用除留余数法构造,用线性探测再散列法或链地址法处理冲突。
哈希表的设计与实现。设计哈希表实现电话号码查询系统。基本要求:(1)设每个记录有一列数据项:电话号码、用户名、地址(2)从键盘输入各记录,分别以电话号码和用户名为关键字建立哈希表;(3)采用再哈希法解决...