【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.rhkb.cn/news/286548.html

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

相关文章

免费客服系统大揭秘!有什么好用的免费客服系统推荐?

贵的不一定是好的&#xff0c;合不合适才最重要&#xff01;有什么好用的免费客服系统吗&#xff1f;现下服务经济的发展的风潮已经席卷到了各行各业。 企业不仅要提供好的产品&#xff0c;还需要好的服务。客服系统作为企业与客户重要的沟通渠道&#xff0c;越来越多的企业正在…

记录些LLM相关的知识

SOTA SOTA是"State-of-the-Art"的缩写&#xff0c;指的是某个技术或领域中目前最先进的技术或方法。在语音合成领域&#xff0c;SOTA语音合成效果指的是使用最新的研究和技术所达到的最佳语音合成效果。这通常包括高清晰度的语音输出&#xff0c;自然的语音流畅度&a…

面试题-Elasticsearch集群架构和调优手段(超全面)

对于Elasticsearch&#xff08;ES&#xff09;&#xff0c;我了解并有经验。在我之前的公司&#xff0c;我们有一个相对大型的ES集群&#xff0c;以下是该集群的架构和一些调优手段的概述&#xff1a; 1. 集群架构 集群规模&#xff1a;我们的ES集群由15个节点组成&#xff0c…

docker配置镜像加速后容器和镜像消失

一、问题描述 根据阿里云给docker配置镜像加速器 sudo mkdir -p /etc/docker sudo tee /etc/docker/daemon.json <<-EOF {"registry-mirrors": ["https://gt6j98xi.mirror.aliyuncs.com"] } EOF sudo systemctl daemon-reload sudo systemctl rest…

框架结构模态分析/动力时程分析Matlab有限元编程 【Matlab源码+PPT讲义】|梁单元|地震时程动画|结果后处理|地震弹性时程分析| 隐式动力学

专栏导读 作者简介&#xff1a;工学博士&#xff0c;高级工程师&#xff0c;专注于工业软件算法研究本文已收录于专栏&#xff1a;《有限元编程从入门到精通》本专栏旨在提供 1.以案例的形式讲解各类有限元问题的程序实现&#xff0c;并提供所有案例完整源码&#xff1b;2.单元…

[NKCTF 2024]web解析

文章目录 my first cms全世界最简单的CTF解法一解法二 my first cms 打开题目在最下面发现是CMS Made Simple&#xff0c;版本为2.2.19 扫一下发现存在后台登陆界面&#xff0c;直接访问 用字典爆破下admin的密码为Admin123 然后直接登录&#xff0c;去漏洞库搜一下其实存在…

Java基于微信小程序的校园请假系统

博主介绍&#xff1a;✌程序员徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#…

python爬虫基础-----运算符(第三天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

C++ 3.25

思维导图&#xff1a;【有道云笔记】无标题脑图(1).mindmap https://note.youdao.com/s/ELJA6sJ6 定义自己的命名空间&#xff0c;其中有string类型的变量&#xff0c;再定义两个函数&#xff0c;一个函数完成字符串的输入&#xff0c;一个函数完成求字符串长度&#xff0c;再定…

python网络爬虫实战教学——requests的使用(2)

文章目录 专栏导读1、POST请求2、响应3、Cookie设置 专栏导读 ✍ 作者简介&#xff1a;i阿极&#xff0c;CSDN 数据分析领域优质创作者&#xff0c;专注于分享python数据分析领域知识。 ✍ 本文录入于《python网络爬虫实战教学》&#xff0c;本专栏针对大学生、初级数据分析工程…

家政服务管理平台设计与实现|SpringBoot+ Mysql+Java+ B/S结构(可运行源码+数据库+设计文档)

本项目包含可运行源码数据库LW&#xff0c;文末可获取本项目的所有资料。 推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;…

css3鼠标悬停图片特效,图片悬停效果源码

特效介绍 css3鼠标悬停图片特效,图片悬停效果源码&#xff0c;可以在网页上面作为自己的动态加载名片&#xff0c;放到侧边栏或者网站合适的位置即可 动态效果 代码下载 css3鼠标悬停图片特效,图片悬停效果源码

docker 进入容器内部命令

docker容器运行了&#xff0c;怎么进入容器内部查看内部的文件情况呢&#xff1f; 答&#xff1a;可以通过docker exec 的命令查看。 docker exec --help 可以查看命令介绍 &#xff1a; docker exec -it XXX /bin/bash XX为容器ID 进入容器内部 /bin/bash是需要添加的 不…

2.6 IDE(集成开发环境)是什么

IDE&#xff08;集成开发环境&#xff09;是什么 IDE 是 Integrated Development Environment 的缩写&#xff0c;中文称为集成开发环境&#xff0c;用来表示辅助程序员开发的应用软件&#xff0c;是它们的一个总称。 通过前面章节的学习我们知道&#xff0c;运行 C 语言&…

JavaWeb:AOP、配置优先级、Bean管理、SpringBoot原理、Maven高级

1 AOP 1.1 基本语法 面向切面编程、面向方面编程&#xff0c;面向特定方法编程 在管理bean对象的过程中&#xff0c;主要通过底层的动态代理机制&#xff0c;对特定的方法进行编程 应用&#xff1a;统计每一个业务方法的执行耗时 xml引入依赖 <!-- AOP-->&l…

2015年认证杯SPSSPRO杯数学建模A题(第一阶段)绳结全过程文档及程序

2015年认证杯SPSSPRO杯数学建模 A题 绳结 原题再现&#xff1a; 给绳索打结是人们在日常生活中常用的技能。对登山、航海、垂钓、野外生存等专门用途&#xff0c;结绳更是必不可少的技能之一。针对不同用途&#xff0c;有多种绳结的编制方法。最简单的绳结&#xff0c;有时称…

数据结构 之 队列习题 力扣oj(附加思路版)

优先级队列 #include<queue> --队列 和 优先级队列的头文件 优先级队列&#xff1a; 堆结构 最大堆 和 最小堆 相关函数&#xff1a; front() 获取第一个元素 back() 获取最后一个元素 push() 放入元素 pop() 弹出第一个元素 size() 计算队列中元素…

深度学习启蒙:神经网络基础与激活函数

目录 1.引言 2.神经网络架构与前向传播 2.1. 神经网络架构 2.2. 前向传播 3.常见激活函数公式与图像 3.1. sigmoid函数 3.2. tanh函数 3.3. ReLU函数 3.4. Leaky ReLU 3.5. Softmax函数 4.激活函数可视化比较与选择 4.1激活函数对比图像 4.1激活函数的选择策略…

C语言:给结构体取别名的4种方法

0 前言 在进行嵌入式开发的过程中&#xff0c;我们经常会见到typedef这个关键字&#xff0c;这个关键字的作用是给现有的类型取别名&#xff0c;在实际使用过程中往往是将一个复杂的类型名取一个简单的名字&#xff0c;便于我们的使用。就像我们给很熟的人取外号一样&#xff…

python3游戏GUI--开心打地鼠游戏By:PyQt5(附下载地址)

文章目录 一&#xff0e;前言二&#xff0e;游戏预览1.启动2.开始游戏3.游戏结束4.排行榜 三&#xff0e;游戏思路四&#xff0e;总结 一&#xff0e;前言 第一次用PyQt做游戏&#xff0c;有点小紧张呢。本次使用PyQt5制作一款简单的打地鼠游戏&#xff0c;支持基本游戏玩法、…