当前位置: 首页 > news >正文

腾讯面试:如何解决哈希冲突?

我们面试时经常被问到HashMap是怎么解决哈希冲突的,很多同学对其含糊其词、一知半解。因此小编对相关知识进行了总结,希望帮助读者加深对其理解。

哈希表就是通过散列函数将键映射到定值,简单来说就是一个键对应一个值。

而通过散列函数映射时将两个键映射到了同一个值,即这两个键将被哈希表映射到同一个位置,这种情况就被称为哈希冲突。

解决哈希冲突通过有四种方法:

  • 链接法
  • 开放寻址法
  • 再哈希法
  • 哈希桶扩容

1. 链接法

每个哈希表的槽位维护一个链表或其他数据结构,当多个元素被哈希到同一个槽位时,它们会被放在这个槽位的链表中。查找时会遍历链表,插入时也会直接加到链表中。

假设我们有一个哈希表,哈希函数将键值映射到以下槽位:

0: [5, 15]1: []2: [2]3: []4: [4]当我们插入键值 5 和 15 时,它们都被映射到槽位 0。因此,它们会形成一个链表:

槽位 0: [5 -> 15]槽位 1: []槽位 2: [2]槽位 3: []槽位 4: [4]

另外:
推荐一个程序员免费学习的编程网站:我爱编程网(www.love-coding.com)
涵盖 Java几乎覆盖了所有主流技术面试题,还有市面上最全的技术精品系列教程,免费提供。
在这里插入图片描述

2. 开放寻址法

当发生冲突时,算法会寻找下一个可用的槽位。常见的探查方式有线性探查、二次探查和双重哈希等。这种方法不使用额外的存储结构,而是在哈希表内部处理所有元素。开放寻址法的几种常见探测方法确实包括线性探测、二次探测和双重散列。以下是每种方法的详细说明和示例:

2.1. 线性探测

在发生冲突时,线性探测会逐个检查后续的槽位,直到找到一个空槽。例如:

假设哈希函数为 h(k) = k % 5

  • 插入 1 → 槽位 1(成功)
  • 插入 6 → 槽位 1(冲突),检查 2(成功)
  • 插入 11 → 槽位 1(冲突),2(冲突),3(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: [6]
槽位 3: [11]
槽位 4: []
2.2. 二次探测

二次探测在发生冲突时采用平方递增的方式查找空槽。例如:

哈希函数仍然假设是 h(k) = k % 5

  • 插入 1 → 槽位 1(成功)
  • 插入 6 → 槽位 1(冲突),检查 1^22(成功)
  • 插入 11 → 槽位 1(冲突),检查 1^2(冲突),2^24(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: []
槽位 3: []
槽位 4: [6]
2.3. 双重哈希

双重散列使用第二个哈希函数来决定步长,以解决冲突。例如:

假设第一个哈希函数 h1(k) = k % 5,第二个哈希函数 h2(k) = 1 + (k % 4)

插入 1 → 槽位 1(成功)插入 6 → 槽位 1(冲突),步长 h2(6) = 1 + (6 % 4) = 3,检查 1 + 3 = 4(成功)插入 11 → 槽位 1(冲突),步长 h2(11) = 1 + (11 % 4) = 3,检查 1 + 3 = 4(冲突),再检查 4 + 3 = 2(成功)

最终哈希表:

槽位 0: []
槽位 1: [1]
槽位 2: [11]
槽位 3: []
槽位 4: [6]

3. 再哈希法

在发生冲突后,可以使用另一个哈希函数对该元素进行再哈希,找到一个新的槽位。

使用初始哈希函数 h1(k) = k % 5,当插入 10 时:

  • h1(10) = 0(槽位 0 已占用)

  • 使用新的哈希函数

    h2(k) = (k / 5) % 5
    

    ,计算:

    • h2(10) = 2(槽位 2 已占用)
  • 再次使用

    h1
    

    计算:

    • h1(10 + 1) = 1(槽位 1 已占用)
    • h1(10 + 2) = 3(放入槽位 3

最终哈希表如下:

槽位 0: [0]
槽位 1: [1]
槽位 2: [2]
槽位 3: [10]
槽位 4: [4]

4. 哈希桶扩容

如果哈希表的负载因子超过某个阈值,可以增加哈希表的大小,并重新计算所有元素的哈希值并重新分配到新的槽位。这有助于减少冲突并提高性能

哈希表的负载因子是一个衡量哈希表填充程度的重要指标,通常用公式表示为:负载因子 = 哈希表中的元素数量 / 哈希表的槽位总数负载因子的意义:高负载因子:当负载因子接近或超过 1 时,表示哈希表的槽位几乎被填满,可能导致更多的哈希冲突,从而影响查找、插入和删除的性能。
低负载因子:负载因子较低时,哈希表的空槽较多,冲突较少,性能较好,但会导致内存浪费。
负载因子的调整:通常,当负载因子超过某个设定的阈值(例如 0.7 或 0.75),就会进行扩容。扩容时,哈希表的槽位数量增加,所有元素需要重新哈希并放入新的槽位中。

假设哈希表的大小为 5,当前负载因子超过 0.7,我们决定扩容到 10。在扩容时,所有元素的哈希值需要重新计算:

  • 原哈希表:
槽位 0: [0]
槽位 1: [1]
槽位 2: [2]
槽位 3: [3]
槽位 4: [4]
  • 扩容后,哈希函数改为 h(k) = k % 10,插入后的哈希表:
槽位 0: []
槽位 1: [1]
槽位 2: [2]
槽位 3: [3]
槽位 4: [4]
槽位 5: [5]
槽位 6: []
槽位 7: []
槽位 8: []
槽位 9: []

5.方法比较

5.1. 链接法

优点:

  • 容易实现,简单明了。
  • 动态性好,可以存储任意数量的元素,只受限于内存。
  • 插入和删除操作较快,不需要重新哈希。

缺点:

  • 在某些情况下,链表可能会很长,导致查找性能下降。如果链表过长,可能导致性能接近于线性查找。
  • 需要额外的内存来存储链表节点。
5.2. 开放寻址法

优点:

  • 所有元素都存储在哈希表内部,节省了额外的内存。
  • 不需要额外的链表,查找时不需要遍历。

缺点:

  • 哈希表的负载因子需要控制在较低水平,通常小于 0.7,否则性能显著下降。
  • 在频繁冲突的情况下,查找效率会下降,且可能需要进行多次探测。
  • 删除操作复杂,可能导致“探测链”的问题,影响后续查找性能。
5.3. 再哈希法

优点:

  • 通过使用不同的哈希函数,能有效地减少冲突。
  • 可与其他方法结合使用,灵活性高。

缺点:

  • 需要额外的计算和存储开销,可能导致性能下降。
  • 在大量元素插入时,可能需要频繁地进行哈希计算。
5.4. 哈希桶扩容

优点:

  • 通过扩容可以有效降低负载因子,从而减少冲突。
  • 能够保持较高的性能,特别是在处理大量数据时。

缺点:

  • 扩容过程可能需要遍历整个哈希表,重新计算哈希值,导致短时间内性能下降。
  • 增加了内存的使用和管理复杂度。

相关文章:

腾讯面试:如何解决哈希冲突?

我们面试时经常被问到HashMap是怎么解决哈希冲突的,很多同学对其含糊其词、一知半解。因此小编对相关知识进行了总结,希望帮助读者加深对其理解。 哈希表就是通过散列函数将键映射到定值,简单来说就是一个键对应一个值。 而通过散列函数映射…...

【动手学运动规划】 4.5 A*算法

我宁愿永远做我自己,也不愿成为别人,即使那个人比你更快乐。 —《成为简奥斯汀》 🏰代码及环境配置:请参考 环境配置和代码运行! 4.5.1 概述 Dijkstra算法是基于广度优先搜索策略来遍历空间内的所有节点,最终计算出…...

Spring Boot 3.4.0 发布:功能概览与示例

Spring Boot 3.4.0 带来了许多增强功能,使现代应用开发更加高效、便捷和强大。以下是最新功能的完整概述,以及一些帮助您快速入门的代码示例。 1. 应用程序版本管理 Spring Boot 引入了 spring.application.version 属性,方便开发者设置和访…...

【48】Android通过libjpeg-turbo库实现图片压缩

(1)公司为节约图片占用服务器存储资源成本,需要对Android手机客户端所传递到云存储服务器中的图片进行压缩,在不影响图片失真程度的情况下,最大限度的压缩图片以节省图片所占用的存储空间。 (2)…...

Linux输入设备应用编程

本章学习输入设备的应用编程,首先要知道什么是输入设备?输入设备其实就是能够产生输入事件的设备就称为输入设备,常见的输入设备包括鼠标、键盘、触摸屏、按钮等等,它们都能够产生输入事件,产生输入数据给计算机系统。…...

【Vulkan入门】03-创建Device

目录 先叨叨git信息关键代码VulkanEnv::CreateDevice() 编译并运行程序题外话 先叨叨 在上篇已经选择了一个合适的PhysicalDevice。 本篇要为这个PhysicalDevice创将一个Device。Device可以理解为APP与PhysicalDevice之间的代理。 所有APP与PhysicalDevice之间交互的资源都通过…...

【jvm】C2编译器

目录 1. 说明2. 编译流程3. 使用与配置4. 性能优化与监控5. 局限性 1. 说明 1.JVM(Java虚拟机)C2编译器是Java编译过程中的重要环节,专门用于将Java字节码编译成高效的本地机器代码,以提升Java程序的执行效率。2.特点&#xff1a…...

使用 Acme.sh 自动生成和续签免费 SSL 证书(含通配符支持)

Acme.sh 是一个开源的脚本,能够从 ZeroSSL、Let’s Encrypt 等证书颁发机构(CA)获取免费的 HTTPS 证书。该脚本特别简单易用,并且支持多种验证方式。下面将详细介绍使用 Acme.sh 生成、安装和更新证书的各个步骤。 Github地址 使用…...

Android 图形系统之四:Choreographer

Choreographer 是 Android 系统中负责帧同步的核心组件,它协调输入事件、动画和绘制任务,以确保界面以固定频率(通常是每 16ms,一帧)流畅渲染。通过管理 VSYNC 信号和调度任务,Choreographer 是实现流畅 UI…...

CAP定理和BASE理论

CAP定理 CAP定理,也称为布鲁尔定理(Brewer’s Theorem),是分布式系统设计中的一个基本原理。它指出在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容…...

笔记软件:我来、思源笔记、Obsidian、OneNote

最近wolai的会员到期了,促使我更新了一下笔记软件。 首先,wolai作为一个笔记软件,我觉得有很多做得不错的方面(否则我也不会为它付费2年了),各种功能集成得很全(公式识别这个功能我写论文的时候…...

试探互联网如何工作?

Reading: How_does_the_Internet_workhow-does-internet-work Watching:How the Internet Works in 5 Minutes Outline: 互联网通过全球互联的计算机和服务器网络工作,通过标准化协议进行通信。数据被分解成数据包,并使用互联…...

【c++笔试强训】(第三十篇)

目录 爱丽丝的⼈偶(贪⼼构造) 题目解析 讲解算法原理 编写代码 集合(排序) 题目解析 讲解算法原理 编写代码 爱丽丝的⼈偶(贪⼼构造) 题目解析 1.题目链接:登录—专业IT笔试面试备考平…...

微信小程序购物车全选反选功能以及合计

微信小程序基于Vant Weapp的购物车功能实现 1、单选 使用微信小程序原生表单组件checkbox和checkbox-group 注意&#xff1a;checkbox原生不支持bind:change事件&#xff0c;checkbox-group支持 <checkbox-group bindchange"handleCheck"><checkbox val…...

vue-qr在线生成二维码组件(vue2版本)

在对于二维码生成中有许多组件&#xff0c;下面介绍关于自定义比较高的vue-qr组件&#xff0c;能自定义设置背景颜色、背景图片、背景Gif图、实点和空白区的颜色、中心Logo的图片和边距。 依赖下载 注意&#xff1a; 直接npm下载最新版 有些项目可能运行会抱错 这时候你可以降…...

大语言模型技术相关知识-笔记整理

系列文章目录 这个系列攒了很久。主要是前段之间面试大语言模型方面的实习&#xff08;被拷打太多次了&#xff09;&#xff0c;然后每天根据面试官的问题进行扩展和补充的这个笔记。内容来源主要来自视频、个人理解以及官方文档中的记录。方便后面的回顾。 文章目录 系列文章…...

SCP命令实现Linux中的文件传输

CP命令的主要作用是实现Linux与Linux系统之间的文件传输。 SCP命令时基于SSH协议,所以两台服务器的sshd服务必须处于开启状态,否则无法完成上传与下载操作。 #1.上传文件 scp linux本地文件路径 远程用户名@linux主机地址:远程路径 #2.下载文件 scp 远程用户名@linux主机地址…...

linux环境中后台运行java程序

在生产环境&#xff0c;我们通常需要让java进程后台运行&#xff0c;并且即使会话关闭&#xff0c;进程也依然存在。 使用的命令&#xff1a; nohup java -jar xxx.jar -> aaa.log 2>&1 & 详细介绍下上面这条命令 &#xff08;1&#xff09;nohup&#xff1a;…...

Go学习:变量

目录 1. 变量的命名 2. 变量的声明 3. 变量声明时注意事项 4. 变量的初始化 5. 简单例子 变量主要用来存储数据信息&#xff0c;变量的值可以通过变量名进行访问。 1. 变量的命名 在Go语言中&#xff0c;变量名的命名规则 与其他编程语言一样&#xff0c;都是由字母、数…...

在Unity编辑模式下运行Mono中的方法

[ExecuteAlways] 最简单的方法当然是直接给Mono加上[ExecuteAlways]修饰&#xff0c;这样Mono中的Awake&#xff0c;Update等等都可以在编辑模式下按照原本的时机运行。 [ExecuteAlways] public class TestScript : MonoBehaviour {void TestMethod(){Debug.Log("TestMe…...

Chapter03-Authentication vulnerabilities

文章目录 1. 身份验证简介1.1 What is authentication1.2 difference between authentication and authorization1.3 身份验证机制失效的原因1.4 身份验证机制失效的影响 2. 基于登录功能的漏洞2.1 密码爆破2.2 用户名枚举2.3 有缺陷的暴力破解防护2.3.1 如果用户登录尝试失败次…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

TDengine 快速体验(Docker 镜像方式)

简介 TDengine 可以通过安装包、Docker 镜像 及云服务快速体验 TDengine 的功能&#xff0c;本节首先介绍如何通过 Docker 快速体验 TDengine&#xff0c;然后介绍如何在 Docker 环境下体验 TDengine 的写入和查询功能。如果你不熟悉 Docker&#xff0c;请使用 安装包的方式快…...

golang循环变量捕获问题​​

在 Go 语言中&#xff0c;当在循环中启动协程&#xff08;goroutine&#xff09;时&#xff0c;如果在协程闭包中直接引用循环变量&#xff0c;可能会遇到一个常见的陷阱 - ​​循环变量捕获问题​​。让我详细解释一下&#xff1a; 问题背景 看这个代码片段&#xff1a; fo…...

day52 ResNet18 CBAM

在深度学习的旅程中&#xff0c;我们不断探索如何提升模型的性能。今天&#xff0c;我将分享我在 ResNet18 模型中插入 CBAM&#xff08;Convolutional Block Attention Module&#xff09;模块&#xff0c;并采用分阶段微调策略的实践过程。通过这个过程&#xff0c;我不仅提升…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

Robots.txt 文件

什么是robots.txt&#xff1f; robots.txt 是一个位于网站根目录下的文本文件&#xff08;如&#xff1a;https://example.com/robots.txt&#xff09;&#xff0c;它用于指导网络爬虫&#xff08;如搜索引擎的蜘蛛程序&#xff09;如何抓取该网站的内容。这个文件遵循 Robots…...

什么是EULA和DPA

文章目录 EULA&#xff08;End User License Agreement&#xff09;DPA&#xff08;Data Protection Agreement&#xff09;一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA&#xff08;End User License Agreement&#xff09; 定义&#xff1a; EULA即…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...