Java基础数据结构之哈希表

概念

顺序结构以及平衡树 中,元素关键码与其存储位置之间没有对应的关系,因此在 查找一个元素时,必须要经过关键 码的多次比较 顺序查找时间复杂度为 O(N) ,平衡树中为树的高度,即 O( log2N ) ,搜索的效率取决于搜索过程中 元素的比较次数。
理想的搜索方法:可以 不经过任何比较,一次直接从表中得到要搜索的元素 如果构造一种存储结构,通过某种函 (hashFunc) 使元素的存储位置与它的关键码之间能够建立一一映射的关系,那么在查找时通过该函数可以很快 找到该元素
当向该结构之中插入元素时,根据该元素的关键码和特定的函数计算出该元素应存放的位置,并且按此位置存放,而在取元素时按同样方式计算出所处位置。这样的话,存储和查找的时间复杂度就可以达到O(1)。
该方式即为哈希(散列)方法,用到的函数称为哈希(散列)函数。构造出来的结构称为哈希表或散列表
哈希函数设置为: hash(key) = key % capacity ; capacity 为存储元素底层空间总的大小。
比如一个长度为10的数组
如果要放13,hash(13)=13%10=3所以放在3下标,但如果要放14,会出现什么问题?

冲突(碰撞)

1.概念:

对于两个数据元素的关键字 和 (i != j) ,有ki != kj ,但有: Hash(ki ) == Hash( kj) ,即: 不同关键字通过相同哈 希函数计算出相同的哈希地址,该种现象称为哈希冲突或哈希碰撞

2.冲突的发生是必然的,我们要做的就是降低冲突率

3.冲突的避免--哈希函数的设计

引起哈希冲突的一个原因可能是: 哈希函数设计不够合理
希函数设计原则
哈希函数的定义域必须包括需要存储的全部关键码,而如果散列表允许有 m 个地址时,其值域必须在 0 m-1 之间
哈希函数计算出来的地址能均匀分布在整个空间中
哈希函数应该比较简单

1.直接定制法(常用)

取关键字的某个线性函数为散列地址: Hash Key = A*Key + B。优点:简单均匀;缺点:需要事先知道关键字的分布情况;使用场景:适合于查找比较小且连续的情况。
例如:Hash(key)=key-minval;对于数据97,95,91,93,96,minval是91,所以将97放到6下标,95放到4下标……
2.除留余数法
散列表中允许的地址数是m(就是下标从0到m,注意哈希表的底层首先是一个数组),那么就取小于等于m,接近于m的质数p作为除数,用函数 hash(key) = key %p来求得地址
3. 平方取中法 --( 了解 )
假设关键字为 1234 ,对它平方就是 1522756 ,抽取中间的 3 227 作为哈希地址; 再比如关键字为 4321 ,对 、它平方就是18671041 ,抽取中间的 3 671( 710) 作为哈希地址 平方取中法比较适合:不知道关键字的分 布,而位数又不是很大的情况
4. 折叠法 --( 了解 )
折叠法是将关键字从左到右分割成位数相等的几部分( 最后一部分位数可以短些 ) ,然后将这几部分叠加求和, 并按散列表表长,取后几位作为散列地址。 折叠法适合事先不需要知道关键字的分布,适合关键字位数比较多的情况
5. 随机数法 --( 了解 )
选择一个随机函数,取关键字的随机函数值为它的哈希地址,即 H(key) = random(key), 其中 random 为随机数函数。 通常应用于关键字长度不等时采用此法
6. 数学分析法 --( 了解 )
设有 n d 位数,每一位可能有 r 种不同的符号,这 r 种不同的符号在各位上出现的频率不一定相同,可能在某些位上分布比较均匀,每种符号出现的机会均等,在某些位上分布不均匀只有某几种符号经常出现。可根据散列表的大小,选择其中各种符号分布均匀的若干位作为散列地址。
注意:哈希函数设计的越精妙,产生哈希冲突的可能性就越低,但是无法避免哈希冲突

4.冲突的避免--负载因子调节

散列表的载荷因子(负载因子)=填入表中的元素/散列表的长度

由于表长是定值,所以填入的元素越多,负载因子越大,产生冲突的可能性就越大。一般要将载荷因子控制在0.75以下,当超过0.75时,就应该对哈希表中的数组进行扩容

5.冲突的解决之闭散列

闭散列:也叫开放地址法,当发生哈希冲突时,如果哈希表未被装满,说明在哈希表中必然还有空位置,那么可以 key 存放到冲突位置中的 下一个 空位置中去。那么如何找到下一个空位置呢?
法一:线性探测
从发生冲突的位置开始,依次向后进行探测,直到找到下一个空位置。缺陷是产生冲突的元素会堆积在一块,例如:
想要插入11,21,31,41,就会依次放到2,3,8,0下标
法二:二次探测
找下一个空位置的方法为:Hi  = (H0+ i^2)% m, 或者: Hi= (H0-i^2 )% m。其中:H0是通过哈希函数计算出的下标, i = 1,2,3… ,表示的是发生冲突的次数,例如
想要放21,通过哈希函数计算出来是1,即H0=1,这是第一次发生冲突,所以i=1,所以 Hi= (H0+i^2 )% m即Hi=(1+1)%10=2。

6.冲突的解决之开散列(哈希桶)

开散列法又叫链地址法 ( 开链法 ) ,首先对关键码集合用散列函数计算散列地址,具有相同地址的关键码归于同一子 集合,每一个子集合称为一个桶,各个桶中的元素通过一个单链表链接起来,各链表的头结点存储在哈希表中。
比如要放11,通过散列函数计算出下标为1,所以可以通过头插法或者尾插法将11查到对应的链表里
这就是我们所说的哈希表实际上是数组+链表+红黑树(当数组长度>=64&&链表长度>=8以后,就会将其变成一棵红黑树)
java的HashMap就是用这种哈希表的方式来解决哈希冲突的

7.哈希桶的实现

首先注意,哈希桶是一个数组,数组的元素其实就是每个链表的头节点

放置元素:

首先要找到对应链表,然后遍历该链表,如果某个结点的key等于待插入结点的key,就更新该节点的value值,然后结束该方法即可;如果没有对应的key,就通过头插法或者尾插法插入该节点,代码如下:

但这样是有缺陷的,我们之前提到了负载因子最好不超过0.75,所以我们再加一个方法返回当前的负载因子,如果大于0.75就进行扩容,代码如下:

首先添加一个成员变量,即默认最大负载因子

然后是计算负载因子

最后是在put方法的最后进行扩容。

但这样的扩容不对!!!因为数组长度变了,对应的key所在的下标就会变!!!,所以代码应为(我用的头插法扩容):

获得value:

HashMap,HasSet的实现

1.Map不支持迭代器遍历,因为它没有实现Iterable接口,要想用迭代器,就必须将Map转化成Set

2.Map中的对象不需要必须可比较,因为他是通过Hash函数来计算所处位置,而不是通过大小比较。并且key或value可以是null,如下:

3.HashMap和HashSet一样是可以天然去重的,(TreeSet,TreeMap也一样)如下:

 

4.HashMap,HashSet对象不一定可比较,key也不一定是一个整数,那么系统是怎么找到对应的下标从而将键值对放进去的呢?这就用到了HashCode方法来生成一个整数。看源码:

当key不是null时,就会调用key的hashCode方法,如果key本身没有hashCode方法,就会调用Object类的方法:

这段话的意思是,在合理情况下,不同的对象会返回不同的整数,这通常是将对象的内部地址转换为整数实现的,所以下面的情况,即使Student的id是相同的,产生的hashCode也是不同的,如下

要想产生同样的整数,就可以重写hashCode方法:

这是我们根据上面的源码自己重写的方法,调用了hash方法,传参时直接传入id,但最好是通过编译器直接生成hashCode方法,同时一并重写equals方法

如果不重写equals方法,相同id的对象jinxingequals方法后产生的结果是false,因为源码中equals是根据对象的地址比较的

哈希桶的优化代码

我们将哈希桶写成一个泛型类,并让其可以通过各种类型的key生成对应的整数,代码如下:

1.放置键值对:

2.根据key得到对应的value

看下面的一段代码:

student1和student2的id是一样的,而且我们重写了hashCode方法,所以说,虽然我们只在哈希桶里面放置了student1,但在根据student2取元素时,得到的也应该是10,但结果却如下:

这是因为put函数和getval函数有一句是错的,即if(cur.key==key),应该用equals方法,代码如下:

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

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

相关文章

防火墙用户认证、NAT、策略路由、DNS透明代理以及双机热备笔记

用户认证 防火墙管理员登录认证 --- 检验身份的合法性,划分身份权限 用户认证 --- 上网行为管理的一部分 用户,行为,流量 --- 上网行为管理三要素 用户认证的分类 上网用户认证 --- 三层认证 --- 所有的跨网段的通信都可以属于上网行为。…

【Web前端实操19】商城官网_分析与顶部广告

本次实操主要是借用小米之前的网站来进行参考,达成网站静态页面开发的目的,而新学者想要一次性直接开发整个网站,肯定会很懵圈,因此,这个商城官网我会一部分一部分地进行拆分来写,最后合成整个界面。 本次…

国企重组整合后,如何严把“选人用人”关?

三年国企改革之后,新一轮国企改革明确重组方向,强调将“战略性重组和专业化整合”作为深化供给侧结构性改革从而建设世界一流企业的重要抓手,推动了国有资源的高效运转流动。在很多企业兼并、重组后,成立了新的集团性公司&#xf…

Mysql查询数据

1 基本查询语句 MySQL从数据表中查询数据的基本语句为SELECT语句。SELECT语句的基本格式是: 2 单表查询 2.1 查询所有字段 SELECT * FROM 表名; 2.2 在SELECT语句中指定所有字段 SELECT f_id, s_id ,f_name, f_price FROM fruits; 2.3 查询单个字段 SELECT 列名FR…

nginx部署前端(vue)项目及配置修改

目录 一、前端应用打包 二、部署前端应用 1、上传前端文件夹 2、修改nginx配置文件 3、重启nginx 三、查看效果 nginx安装参考:linux安装nginx-CSDN博客 一、前端应用打包 打包命令 npm run build 打包成功如下,会在项目路径下生成dist文件夹 二…

Windows Server 2003 Web服务器搭建

系列文章目录 目录 系列文章目录 前言 一、Web服务器是什么? 二、配置服务器 1.实验环境搭建 2.服务器搭建 1)控制面板中找到增加或删除程序打开 2)点击增加程序 3)安装Web服务器 4)查看安装是否成功 5)打开Internet信息服务(IIS)管理器,进行配置 6)找…

【开源之美】:cppcheck

一、项目链接 https://github.com/danmar/cppcheck/tree/main 二、效果示例

图像复原的天花板在哪里?SUPIR:开创性结合文本引导先验和模型规模扩大

SUPIR(Scaling-UP Image Restoration),这是一种开创性的图像复原方法,利用生成先验和模型扩大规模的力量。通过利用多模态技术和先进的生成先验,SUPIR在智能和逼真的图像复原方面取得了重大进展。作为SUPIR中的关键催化…

如何配置MacLinuxWindows环境变量

这里写目录标题 什么是环境变量什么是PATH为什么要配置环境变量 如何配置环境变量环境变量有哪些环境变量加载顺序环境变量加载详解 配置参考方法一: export PATHLinux环境变量配置方法二:vim ~/.bashrcLinux环境变量配置方法三:vim ~/.bash_…

go语言基础之泛型

1.泛型 泛型是一种独立于所使用的特定类型的编写代码的方法。使用泛型可以编写出适用于一组类型中的任何一种的函数和类型。 1.1 为什么需要泛型 func reverse(s []int) []int {l : len(s)r : make([]int, l)for i, e : range s {r[l-i-1] e}return r }fmt.Println(reverse…

红外热成像仪定制_热成像仪/红外夜视仪开发方案

红外热成像技术是一种利用红外热成像仪将物体发出的不可见红外辐射能量转换成可见的温度场图像的技术,通过不同颜色来表示不同温度。这项技术的应用领域非常广泛,从电路维修到暖通检测再到汽车故障排查等各个领域都有着重要的作用。 红外热成像仪的解决方…

SpringBoot项目实现热部署的配置方法

SpringBoot项目实现热部署的配置方法 1、什么是热部署? 热部署,就是在应用正在运行的时候升级软件,却不需要重新启动应用。 2、什么是SpringBoot热部署? SpringBoot热部署就是在项目正在运行的时候修改代码, 却不需要重新启动…

PCB的通孔,盲孔,埋孔

通孔:双层板从顶层到底层的打通,这样电流就能够从顶层到底层 盲孔:因为看不到底,像一口井一样,只能打到中间,里面灌上铜,我们可以从第一层切换到第二层,第三层等等,盲孔…

在Windows上安装与配置Apache服务并结合内网穿透工具实现公网远程访问本地内网服务

文章目录 前言1.Apache服务安装配置1.1 进入官网下载安装包1.2 Apache服务配置 2.安装cpolar内网穿透2.1 注册cpolar账号2.2 下载cpolar客户端 3. 获取远程桌面公网地址3.1 登录cpolar web ui管理界面3.2 创建公网地址 4. 固定公网地址 前言 Apache作为全球使用较高的Web服务器…

[React源码解析] Fiber

在React15及以前, Reconciler采用递归的方式创建虚拟Dom, 但是递归过程不可以中断, 如果组件的层级比较深的话, 递归会占用线程很多时间, 那么会造成卡顿。 为了解决这个问题, React16将递归的无法中断的更新重构为异步的可中断更新, Fiber架构诞生。 文章目录 1.Fiber的结构2…

【blender烘焙】法线烘焙出现大面积结构丢失怎么办?blender烘焙vs八猴烘焙

用dcc烘焙法线是很常用的减面优化手段,很多建模的dcc自己也内置的烘焙的功能,像我自己在工作流中也偶尔用blender的烘焙做一下材质的整合优化,在质量要求不高的时候还算凑合可用。 问题描述 在前期的文章中飞燕2号建模,我就遇到…

数据防泄密方案公司(dlp数据防泄密厂商排名)

在当今数字化时代,数据已经成为了企业最重要的资产之一。然而,随着企业信息化的不断深入,数据泄露的风险也越来越大。为了保护企业的核心数据,越来越多的企业开始重视数据防泄密工作,并寻求专业的数据防泄密方案提供商…

LeetCode——415. 字符串相加

C开头 😶‍🌫️😶‍🌫️😶‍🌫️😶‍🌫️Take your time ! 😶‍🌫️😶‍🌫️😶‍🌫️😶‍&#…

【计网·湖科大·思科】实验五 IPV4地址-分类地址和构建超网

🕺作者: 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux 😘欢迎关注:👍点赞🙌收藏✍️留言 🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要&…

CUDA编程的框架-以向量相加为例

CPU适合控制GPU程序的逻辑结构。 注意在编程时要区分CPU的程序和GPU的程序&#xff0c;CPU的内存和GPU的内存。 host- CPU device- GPU CPU的内存和GPU的内存之间是相互独立的&#xff0c;因此需要进行通信。 __global__ //核函数的声明符号<<<grid,block>>>…