哈希函数:它把一个大范围的数字哈希(转化)成一个小范围的数字,这个小范围的数对应着数组的下标。使用哈希函数向数组插入数据后,这个数组就是哈希表。

冲突

当冲突产生时,一个方法是通过系统的方法找到数组的一个空位,并把这个单词填入,而不再用哈希函数得到数组的下标,这种方法称为开放地址法

组的每个数据项都创建一个子链表或子数组,那么数组内不直接存放单词,当产生冲突时,新的数据项直接存放到这个数组下标表示的链表中,这种方法称为链地址法

开放地址法

线性探测: 它沿着数组下标一步一步顺序的查找空白单元。

二次探测: 思想是探测相距较远的单元,而不是和原始位置相邻的单元。

再哈希法:再来一次Hash找位置

链地址法

自己写“Hash”

线性探测

public class MyHashTable {
private DataItem[] hashArray; //DataItem类,表示每个数据项信息
private int arraySize;//数组的初始大小
private int itemNum;//数组实际存储了多少项数据
private DataItem nonItem;//用于删除数据项 public MyHashTable(int arraySize){
this.arraySize = arraySize;
hashArray =new DataItem[arraySize];
nonItem =new DataItem(-1);//删除的数据项下标为-1
}
//判断数组是否存储满了
public boolean isFull(){
return (itemNum == arraySize);
} //判断数组是否为空
public boolean isEmpty(){
return (itemNum ==0);
} //打印数组内容
public void display(){
System.out.println("Table:");
for(int j =0 ; j < arraySize ; j++){
if(hashArray[j] !=null){
System.out.print(hashArray[j].getKey() +" ");
}else{
System.out.print("** ");
}
}
}
//通过哈希函数转换得到数组下标
public int hashFunction(int key){
return key%arraySize;
} //插入数据项
public void insert(DataItem item){
if(isFull()){
//扩展哈希表
System.out.println("哈希表已满,重新哈希化...");
extendHashTable();
}
int key = item.getKey();
int hashVal = hashFunction(key);
while(hashArray[hashVal] !=null && hashArray[hashVal].getKey() != -1){
++hashVal;
hashVal %= arraySize;
}
hashArray[hashVal] = item;
itemNum++;
}
/**
* 数组有固定的大小,而且不能扩展,所以扩展哈希表只能另外创建一个更大的数组,然后把旧数组中的数据插到新的数组中。
* 但是哈希表是根据数组大小计算给定数据的位置的,所以这些数据项不能再放在新数组中和老数组相同的位置上。
* 因此不能直接拷贝,需要按顺序遍历老数组,并使用insert方法向新数组中插入每个数据项。
* 这个过程叫做重新哈希化。这是一个耗时的过程,但如果数组要进行扩展,这个过程是必须的。
*/
public void extendHashTable(){
int num = arraySize;
itemNum =0;//重新计数,因为下面要把原来的数据转移到新的扩张的数组中
arraySize *=2;//数组大小翻倍
DataItem[] oldHashArray = hashArray;
hashArray =new DataItem[arraySize];
for(int i =0 ; i < num ; i++){
insert(oldHashArray[i]);
}
} //删除数据项
public DataItem delete(int key){
if(isEmpty()){
System.out.println("Hash Table is Empty!");
return null;
}
int hashVal = hashFunction(key);
while(hashArray[hashVal] !=null){
if(hashArray[hashVal].getKey() == key){
DataItem temp = hashArray[hashVal];
hashArray[hashVal] = nonItem;//nonItem表示空Item,其key为-1
itemNum--;
return temp;
}
++hashVal;
hashVal %= arraySize;
}
return null;
} //查找数据项
public DataItem find(int key){
int hashVal = hashFunction(key);
while(hashArray[hashVal] !=null){
if(hashArray[hashVal].getKey() == key){
return hashArray[hashVal];
}
++hashVal;
hashVal %= arraySize;
}
return null;
} public static class DataItem{
private int iData;
public DataItem(int iData){
this.iData = iData;
}
public int getKey(){
return iData;
}
} }

再Hash

public class HashDouble {
private DataItem[] hashArray; //DataItem类,表示每个数据项信息
private int arraySize;//数组的初始大小
private int itemNum;//数组实际存储了多少项数据
private DataItem nonItem;//用于删除数据项 public HashDouble(){
this.arraySize =13;
hashArray =new DataItem[arraySize];
nonItem =new DataItem(-1);//删除的数据项下标为-1
}
//判断数组是否存储满了
public boolean isFull(){
return (itemNum == arraySize);
} //判断数组是否为空
public boolean isEmpty(){
return (itemNum ==0);
} //打印数组内容
public void display(){
System.out.println("Table:");
for(int j =0 ; j < arraySize ; j++){
if(hashArray[j] !=null){
System.out.print(hashArray[j].getKey() +" ");
}else{
System.out.print("** ");
}
}
}
//通过哈希函数转换得到数组下标
public int hashFunction1(int key){
return key%arraySize;
} public int hashFunction2(int key){
return 5 - key%5;
} //插入数据项
public void insert(DataItem item){
if(isFull()){
//扩展哈希表
System.out.println("哈希表已满,重新哈希化...");
extendHashTable();
}
int key = item.getKey();
int hashVal = hashFunction1(key);
int stepSize = hashFunction2(key);//用第二个哈希函数计算探测步数
while(hashArray[hashVal] !=null && hashArray[hashVal].getKey() != -1){
hashVal += stepSize;
hashVal %= arraySize;//以指定的步数向后探测
}
hashArray[hashVal] = item;
itemNum++;
} /**
* 数组有固定的大小,而且不能扩展,所以扩展哈希表只能另外创建一个更大的数组,然后把旧数组中的数据插到新的数组中。
* 但是哈希表是根据数组大小计算给定数据的位置的,所以这些数据项不能再放在新数组中和老数组相同的位置上。
* 因此不能直接拷贝,需要按顺序遍历老数组,并使用insert方法向新数组中插入每个数据项。
* 这个过程叫做重新哈希化。这是一个耗时的过程,但如果数组要进行扩展,这个过程是必须的。
*/
public void extendHashTable(){
int num = arraySize;
itemNum =0;//重新计数,因为下面要把原来的数据转移到新的扩张的数组中
arraySize *=2;//数组大小翻倍
DataItem[] oldHashArray = hashArray;
hashArray =new DataItem[arraySize];
for(int i =0 ; i < num ; i++){
insert(oldHashArray[i]);
}
} //删除数据项
public DataItem delete(int key){
if(isEmpty()){
System.out.println("Hash Table is Empty!");
return null;
}
int hashVal = hashFunction1(key);
int stepSize = hashFunction2(key);
while(hashArray[hashVal] !=null){
if(hashArray[hashVal].getKey() == key){
DataItem temp = hashArray[hashVal];
hashArray[hashVal] = nonItem;//nonItem表示空Item,其key为-1
itemNum--;
return temp;
}
hashVal += stepSize;
hashVal %= arraySize;
}
return null;
} //查找数据项
public DataItem find(int key){
int hashVal = hashFunction1(key);
int stepSize = hashFunction2(key);
while(hashArray[hashVal] !=null){
if(hashArray[hashVal].getKey() == key){
return hashArray[hashVal];
}
hashVal += stepSize;
hashVal %= arraySize;
}
return null;
}
public static class DataItem{
private int iData;
public DataItem(int iData){
this.iData = iData;
}
public int getKey(){
return iData;
}
}
}

参考链接

https://www.cnblogs.com/ysocean/p/8032656.html

Hash冲突以及解决的更多相关文章

  1. hash 冲突及解决办法。

    hash 冲突及解决办法. 关键字值不同的元素可能会映象到哈希表的同一地址上就会发生哈希冲突.解决办法: 1)开放定址法:当冲突发生时,使用某种探查(亦称探测)技术在散列表中形成一个探查(测)序列.沿 ...

  2. Hash冲突的解决--暴雪的Hash算法

    Hash冲突的解决--暴雪的Hash算法https://usench.iteye.com/blog/2199399https://www.bbsmax.com/A/kPzOO7a8zx/

  3. Cuckoo Hash——Hash冲突的解决办法

    参考文献: 1.Cuckoo Filter hash算法 2.cuckoo hash 用途: Cuckoo Hash(布谷鸟散列).问了解决哈希冲突的问题而提出,利用较少的计算换取较大的空间.占用空间 ...

  4. Hash冲突的解决方法

    虽然我们不希望发生冲突,但实际上发生冲突的可能性仍是存在的.当关键字值域远大于哈希表的长度,而且事先并不知道关键字的具体取值时.冲突就难免会发 生.另外,当关键字的实际取值大于哈希表的长度时,而且表中 ...

  5. 关于hash冲突的解决

    分离链接法:public class SeparateChainingHashTable<AnyType>{ private static final int DEFAULT_TABLE_ ...

  6. hash冲突解决和javahash冲突解决

    其实就是四种方法的演变 1.开放定址法 具体就是把数据的标志等的对长度取模 有三种不同的取模 线性探测再散列 给数据的标志加增量,取模 平方探测再散列 给数据的标志平方,取模 随机探测再散列 把数据的 ...

  7. Map之HashMap的get与put流程,及hash冲突解决方式

    在java中HashMap作为一种Map的实现,在程序中我们经常会用到,在此记录下其中get与put的执行过程,以及其hash冲突的解决方式: HashMap在存储数据的时候是key-value的键值 ...

  8. hash冲突随笔

    一:hash表 也叫散列表,以key-value的形式存储数据,就是将需要存储的关键码值通过hash函数映射到表中的位置,可加快访问速度. 二:hash冲突 如果两个相同的关键码值通过hash函数映射 ...

  9. 链表法解决hash冲突

    /* @链表法解决hash冲突 * 大单元数组,小单元链表 */ #pragma once #include <string> using namespace std; template& ...

随机推荐

  1. 【黑马程序员C++ STL】学习记录

    黑马程序员 2017 C++ STL 教程(STL 部分已完结) 基于黑马STL课程整理:黑马程序员2017C++STL教程 视频链接 专栏:本STL专栏目录 文章目录 黑马程序员 2017 C++ ...

  2. 权限命令 vi编辑器 命令模式 使用技巧

    一.长格式由七部分组成 权限       引用数字 所有者 所属组 文件大小       时间 名称  如 dr-xr-xr-x     5         root   root       409 ...

  3. Grafana镜像在阿里云镜像站首发上线

    阿里云镜像站体验官招募中, 在各大社区平台分享相关内容累计积分就可赢得Airpods耳机和移动硬盘等奖励,银牌体验官的奖励人数不设限哦.立即参与 简介 Grafana是一个跨平台的开源的度量分析和可视 ...

  4. 韦东山 嵌入式linux教程 笔记

    @ 目录 资源链接 一.常用命令 二.shell 三.如何更改PATH? 四.路径 五.vi编辑器 六.进阶命令 七.NAT配置网络 (第2篇-P34) 八.开发板挂载 Ubuntu 的 NFS 目录 ...

  5. 磁盘管理--如何在VMware上给centos7增加一块磁盘

    一. 实验环境 VMware Workstaion + Centos7 二.实验步骤 1.关闭虚拟机,添加磁盘 2.添加硬件向导 硬件类型 你要安装哪类硬件? 点击完成观察到已经成功添加磁盘 3.开启 ...

  6. Git-rebase使用原理

    使用 Git 已经好几年了,却始终只是熟悉一些常用的操作.对于 Git Rebase 却很少用到,直到这一次,不得不用. 一.起因 上线构建的过程中扫了一眼代码变更,突然发现,commit 提交竟然多 ...

  7. url斜杠问题——重定向

    path('hello',hello), path('hello/',hello), 有什么区别? 没有斜杠:只能访问hello 有斜杠:可以访问hello和hello/ 分析有斜杠的: hello- ...

  8. 学习廖雪峰的Git教程3--从远程库克隆以及分支管理

    一.远程库克隆 这个就比较简单了, git clone git@github.com:****/Cyber-security.git 远程库的地址可以在仓库里一个clone or download的绿 ...

  9. 为什么Java中不支持多重继承?

    我发现这个 Java 核心问题很难回答,因为你的答案可能不会让面试官满意,在大多数情况下,面试官正在寻找答案中的关键点,如果你提到这些关键点,面试官会很高兴.在 Java 中回答这种棘手问题的关键是准 ...

  10. mq 的缺点 ?

    系统可用性降低 系统引入的外部依赖越多,越容易挂掉,本来你就是 A 系统调用 BCD 三个系统的 接口就好了,人 ABCD 四个系统好好的,没啥问题,你偏加个 MQ 进来,万一 MQ 挂了咋整?MQ ...