【Java】哈希表

文章目录

  • 一、概念
  • 二、哈希冲突
    • 2.1概念
    • 2.2设计合理的哈希函数-避免冲突
    • 2.3调节负载因子-避免冲突
    • 2.4闭散列-冲突解决(了解)
    • 2.5开散列/哈希桶-冲突解决(重点掌握)
  • 三、代码实现
    • 3.1成员变量及方法的设定
    • 3.2插入
    • 3.3重新哈希
    • 3.4 获取到value的值


一、概念

不经过任何比较,一次直接从表中得到要搜索的元素。 如果构造一种存储结构,通过某种函
数(hashFunc)使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快找到该元素

当向该结构中:
插入元素
根据待插入元素的关键码,以此函数计算出该元素的存储位置并按此位置进行存放
搜索元素
对元素的关键码进行同样的计算,把求得的函数值当做元素的存储位置,在结构中按此位置取元素比较,若关键码相等,则搜索成功
该方式即为哈希(散列)方法,哈希方法中使用的转换函数称为哈希(散列)函数,构造出来的结构称为哈希表(HashTable)(或者称散列表)

举例:数组元素{1,7,6,4,5,9}
在这里插入图片描述
用该方法进行搜索不必进行多次关键码的比较,因此搜索的速度比较快

二、哈希冲突

2.1概念

当我们插入11的时候,会发现通过哈希函数计算出的要插入的位置等于1,而此时该位置已经有元素存在,即:不同关键字通过相同哈希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞

2.2设计合理的哈希函数-避免冲突

首先,我们需要明确一点,由于我们哈希表底层数组的容量往往是小于实际要存储的关键字的数量的,这就导致一个问题,冲突的发生是必然的,但我们能做的应该是尽量的降低冲突率
引起哈希冲突的一个原因可能是:哈希函数设计不够合理
哈希函数设计原则:
哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有m个地址时,其值域必须在0到m-1之间
哈希函数计算出来的地址能均匀分布在整个空间中
哈希函数应该比较简单
常用的哈希函数
1.直接定制法
取关键字的某个线性函数为散列地址:Hash(Key)= A*Key + B 优点:简单、均匀 缺点:需要事先知道关键字的分布情况 使用场景:适合查找比较小且连续的情况
2.除留余数法
设散列表中允许的地址数为m,取一个不大于m,但最接近或者等于m的质数p作为除数,按照哈希函数:Hash(key) = key% p(p<=m),将关键码转换成哈希地址
以上两种是比较常用的方法,还有其他方法感兴趣的可以自行查阅相关资料
注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

2.3调节负载因子-避免冲突

负载因子 α = 哈希表中元素个数 / 哈希表的长度
哈希表的长度没有扩容是定长的,即 α 与 元素的个数是成正比的,当 α 越大,即代码哈希表中的元素个数越多,元素越多,发生哈希冲突的概率就增加了,因此 α 越小,哈希冲突的概率也就越小。所以我们应该严格控制负载因子的大小,在 Java 中,限制了负载因子最大为 0.75,当超过了这个值,就要进行扩容哈希表,重新哈希(重新将各个元素放在新的位置上)

当冲突率达到一个无法忍受的程度时,我们需要通过降低负载因子来变相的降低冲突率
已知哈希表中已有的关键字个数是不可变的,那我们能调整的就只有哈希表中的数组的大小

2.4闭散列-冲突解决(了解)

解决哈希冲突两种常见的方法是:闭散列和开散列
这里我们先讲闭散列
闭散列:也叫开放定址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以把key存放到冲突位置中的“下一个” 空位置中去。 那么如何寻找下一个空位置呢?
1.线性探测
线性探测:从发生冲突的位置开始,依次向后探测,直到寻找到下一个空位置为止
插入
通过哈希函数获取待插入元素在哈希表中的位置
如果该位置中没有元素则直接插入新元素,如果该位置中有元素发生哈希冲突,使用线性探测找到
下一个空位置,插入新元素
采用闭散列处理哈希冲突时,不能随便物理删除哈希表中已有的元素,若直接删除元素会影响其他
元素的搜索。比如删除元素1,如果直接删除掉,11查找起来可能会受影响。因此线性探测采用标
记的伪删除法来删除一个元素
2.二次探测
线性探测的缺陷是产生冲突的数据堆积在一块,这与其找下一个空位置有关系,因为找空位置的方式就是挨着往后逐个去找,因此二次探测为了避免该问题,找下一个空位置的方法为: Hi= ( H0+i^2 )% m, 或者:Hi= (H0 - i ^2)% m。其中:i = 1,2,3…, H0是通过散列函数Hash(x)对元素的关键码 key 进行计算得到的位置,m是表的大小
当表的长度为质数且表装载因子a不超过0.5时,新的表项一定能够插入,而且任何一个位置都不
会被探查两次。因此只要表中有一半的空位置,就不会存在表满的问题。在搜索时可以不考虑表装满的情况,但在插入时必须确保表的装载因子a不超过0.5,如果超出必须考虑增容
所有:闭散列最大的缺陷就是空间利用率比较低,这也是哈希的缺陷

2.5开散列/哈希桶-冲突解决(重点掌握)

开散列法又叫链地址法(开链法),首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中,简单来说就是数组+链表的结构
如图所示:
在这里插入图片描述
从上图可以看出,开散列中每个桶中放的都是发生哈希冲突的元素。
开散列,可以认为是把一个在大集合中的搜索问题转化为在小集合中做搜索了

三、代码实现

3.1成员变量及方法的设定

public class HashBuck2 <K,V>{static class Node<K,V> {public K key;public V val;public Node<K,V> next;public Node(K key, V val) {this.key = key;this.val = val;}}public Node<K,V>[] array = (Node<K,V>[])new Node[10];//当前存储的元素个数public int usedSize;//负载因子public static final double  LOAD_FACTOR = 0.75;private double doLoadFactor() {return usedSize*1.0 / array.length;}

这里我们采用数组来存储我们的数据,而每个数组的元素是 Node这样的节点,节点中包含 next 引用,用来存放下一个节点,从而实现数组中每个元素可以是一个链表的结构
如图所示:
在这里插入图片描述

3.2插入

这里的插入有两种情况
1.通过 hash 值,得到哈希表的位置上不存在元素,也就是 hash 位置为 null 的情况下
直接在当前位置new一个节点进行插入
2.通过 hash 值,得到哈希表的位置上已经存在元素了,也就是 hash 位置 不为 null 的情况下
遍历链表如果没有与要插入元素相同,就直接采用头插或者尾插
如果遍历链表发现有与要插入元素相同,直接修改该元素所对应的value值就可以了

代码如下:

    public void push(K key,V val) {Node<K,V> node = new Node<K,V>(key, val);//找到位置int hash = key.hashCode();int index = hash % array.length;//遍历数组Node<K,V> cur = array[index];while (cur != null) {if(cur.key.equals(key)) {//对val进行更新cur.val = val;return;}cur = cur.next;}//头插法node.next = array[index];array[index] = node;usedSize++;if( doLoadFactor() >= 0.75) {//重新哈希reSize();}}

以上采用的是头插法,这里每插入一个元素都要判断是否超出了我们设定的负载因子,如果超出了就要重新调整哈希表的长度

3.3重新哈希

哈希表的长度发生改变,表中元素key所对应的hash值也会发生改变,所以扩容之后,原来表中所有元素的位置都要通过新的 hash 值放入到新的位置上,再把新的数组拷贝回原来的数组

代码如下:

    private void reSize() {Node[] newArray = new Node[array.length*2];//处理重新哈希for (int i = 0; i < array.length; i++) {Node cur = array[i];while (cur != null) {int index = cur.key.hashCode() % newArray.length;//记录下来 之前的cur.nextNode curNext = cur.next;//进行头插法,插入到新数组cur.next = newArray[index];newArray[index] = cur;cur = curNext;}}//把数据给到原数组 arrayarray = newArray;}

3.4 获取到value的值

通过key获取到index的位置,这个位置可能没有元素,可能是一条链表,但链表中也可能不存在key,也可能存在 key,如果 index 位置没有元素,或者遍历 index 位置都没找到 key,那么就返回 null,找到了即返回 key 对应的 value 值即可

代码如下:

    public V get(K key) {//找到数组中index的位置int hash = key.hashCode();int index = hash % array.length;//遍历数组中的每个链表Node<K,V> cur = array[index];while (cur != null) {if(cur.key.equals(key)) {return  cur.val;}cur = cur.next;}return null;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/771376.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

YT8531调试记录

总结 还是从设备树&#xff0c;mac驱动&#xff0c;mac驱动对mdio总线的注册&#xff0c;phy驱动 &#xff0c;phy的datasheet&#xff0c;cpu的datasheet 几个方面来看来看 0.确认供电&#xff0c;以及phy的地址(一般会有多个地址&#xff0c;根据相关引脚电平可配置) 1.确…

第二十九天-Flask框架web开发

目录 1.介绍 2.安装 虚拟环境安装 3.使用 1.第一个Flask程序 2.MTV模式 3.启动选项以及调试 启动 调试模式 Pycharm启动配置 4.Flask的扩展 5.url配置和路由 6.响应上下文对象 ​编辑7.请求保报文常用参数 8.响应报文 9.重定向等内部视图 1.介绍 网址&#xff1…

BEVFormer v2论文阅读

摘要 本文工作 提出了一种具有透视监督&#xff08;perspective supervision&#xff09;的新型鸟瞰(BEV)检测器&#xff0c;该检测器收敛速度更快&#xff0c;更适合现代图像骨干。现有的最先进的BEV检测器通常与VovNet等特定深度预训练的主干相连&#xff0c;阻碍了蓬勃发展…

Diffuison在域自适应中 笔记

1 Title Diffusion-based Target Sampler for Unsupervised Domain Adaptation&#xff08;Zhang, Yulong, Chen, Shuhao, Zhang, Yu, Lu, Jiang&#xff09;【CVPR 2023】 2 Conclusion large domain shifts and the sample scarcity in the target domain make exis…

LeetCode:2642. 设计可以求最短路径的图类(SPFA Java)

目录 2642. 设计可以求最短路径的图类 题目描述&#xff1a; 实现代码与解析&#xff1a; SPFA 原理思路&#xff1a; 2642. 设计可以求最短路径的图类 题目描述&#xff1a; 给你一个有 n 个节点的 有向带权 图&#xff0c;节点编号为 0 到 n - 1 。图中的初始边用数组 e…

【开发篇】六、查询大量数据导致内存溢出

文章目录 1、溢出场景2、快照文件分析3、本地环境复现4、结论5、解决思路 记录一个问题&#xff0c;工作中有个数据处理服务OOM&#xff0c;查了下镜像的dockerfile&#xff0c;发现JVM参数如下。很明显&#xff0c;一个数据服务&#xff0c;里面经手大量的数据对象&#xff0c…

el-table 表格中插入表单循环校验

<template><div>{{form}}<el-form :model"form" ref"form"><el-form-item label"呃呃呃呃呃呃呃"><el-table :data"tableData" border><el-table-column prop"time" label"日期"…

JavaWeb项目——MVC架构框架

表现层&#xff08;UI&#xff09;&#xff1a;直接跟前端打交互&#xff08;一是接收前端ajax请求&#xff0c;二是返回json数据给前端&#xff09;业务逻辑层&#xff08;BLL&#xff09;&#xff1a;一是处理表现层转发过来的前端请求&#xff08;也就是具体业务&#xff09…

回溯组合求和算法---去重

给定一个数组 candidates 和一个目标数 target &#xff0c;找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。 说明&#xff1a; 所有数字&#xff08;包括目标数&#xff09;都是正整数。解集不能包含重复的组合。 …

Ollama部署马斯克Grok-1模型

llama.cpp支持 近日llama.cpp添加了对grok-1模型的支持。 PR 6404 GGUF模型 并且在Hugging Face上有大佬放出了Grok-1的非官方GGUF量化模型。 目前可供下载的量化模型: Ollama支持 在ollama官网&#xff0c;有大佬也已经上传了Grok-1模型。 资源充足的各位可以选择适合自…

【C++】详解 to_string 与 to_stoi 函数(整数转字符串-字符串转整数)

目录 一、前言 二、什么是 to_string - to_stoi 三、to_string ✨作用 ✨测试代码&#xff1a; 四、to_stoi ✨作用 ✨测试代码&#xff1a; 五、力扣常考面试题 ✨ 例题 1 ✨例题 2 六、共勉 一、前言 想必大家在做 LeetCode 算法题的时候会经常看到有使用 to_string 和…

Linux(centos7)部署hadoop集群

部署环境要求:已完成JDK环境部署、配置完成固定IP、SSH免费登录、防火墙关闭等。 1、下载、上传主机 官网:https://hadoop.apache.org 2、解压缩、创建软连接 解压: tar -zxvf hadoop-3.3.6.tar.gz软连接: ln -s /usr/local/apps/hadoop-3.3.6 hadoop3、文件配置 hadoo…

【分解定理】分解定理I、II、III

分解定理I 设&#xff0c;则 设&#xff0c;则 分解定理II 设&#xff0c;则 设&#xff0c;则 分解定理III 设&#xff0c;集值映射 且对任意的&#xff0c;有&#xff0c;则 1. 2.设&#xff0c;若&#xff0c;则 3.若&#xff0c;则&#xff1b;若&#xff0c;则 小结…

微信支付服务商处理消费者投诉管理,支持多服务商

大家好&#xff0c;我是小悟 1、问题背景 玩过微信支付生态的&#xff0c;或许就有这种感受&#xff0c;如果收到投诉单&#xff0c;不会通知到手机端&#xff0c;在服务商模式下&#xff0c;只会在微信支付服务商平台-合作伙伴功能-投诉处理那里显示。那你能一直盯着电脑看吗…

nandgame中的寄存器

只有当st and cl 1时&#xff0c;d0 d1的数据通路才会打通。 修改为&#xff1a;st决定通路是否联通&#xff0c;cl从0到1决定一次赋值&#xff08;数据传递&#xff09;。

Linux——进程程序替换

替换原理 用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用一种exec函数 以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动 例程开始执行。调用exec并不创建新进程,所以调用…

RWTH-PHOENIX Weather数据集模型说明和下载

RWTH-PHOENIX Weather 2014 T数据集说明: 德国公共电视台PHOENIX在三年内(2009 年至 2011 年) 录制了配有手语翻译的每日新闻和天气预报节目,并使用注释符号转录了 386 个版本的天气预报。 此外,我们使用自动语音识别和手动清理来转录原始德语语音。因此,该语料库允许训练…

Blast Layer2集成Covalent数据集,提升以太坊dApps拓展能力

Covalent Network&#xff08;CQT&#xff09; 作为行业领先的多链索引器&#xff0c;正着手与 Blast 进行一项激动人心的合作。Blast 是一个独特的 Layer2 扩展方案&#xff0c;旨在解决以太坊网络所面临的可扩展性挑战。目前&#xff0c;Covalent Network&#xff08;CQT&…

期货开户的几个阶段和境界

期市论剑&#xff0c;谁是英雄&#xff0c;每个在期货市场上的人们无时不刻不在努力成为市场上的高手之列&#xff0c;可是期货市场和经济原理是一样的&#xff0c;市场上的人们依水平高低从上至下以金宝塔式排列&#xff0c;利益则成倒倒金字塔排列&#xff0c;也就是塔尖上的…

ensp中pc机访问不同网络的服务器

拓扑图如下&#xff0c;资源已上传 说明&#xff1a;pc通过2个路由访问server服务器 三条线路分别是192.168.1.0网段&#xff0c;192.168.2.0网段和192.168.3.0网段&#xff0c;在未配置的情况下&#xff0c;pc设备是访问不到server的 具体操作流程 第一&#xff1b;pc设备…