数据结构与算法之美-散列表-极客时间笔记

2024-05-31 04:48

本文主要是介绍数据结构与算法之美-散列表-极客时间笔记,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

散列表或者哈希表,就是数组的一种延伸应用,主要还是利用数组可以方便的理由下标索引进行元素操作。概念的核心点在于原始数据可能是键值对,将Key值通过哈希函数进行转化变成唯一(希望是唯一,但并无法保证,所以才有后面的散列冲突问题)的下标索引,以此来保证Value值。

 散列函数的设计有下列三点要求:

1、散列函数计算得到的散列值是一个非负整数;——因为散列的结果对应数组下标,必要要求非负整数。

2、如果 key1 = key2,那 hash(key1) == hash(key2);——相同Key对应的散列值要一致,涉及第一次存储和后续读取

3、如果 key1 ≠ key2,那 hash(key1) ≠ hash(key2)。——最好的结果是不同的Key对应不同的散列值,这样才能区分

但第三点是没办法实现的,因为无论如何数组大小是有限的,而可能的Key是无限的,这样用有限去对应无限,必然是会出现重复,这就是所谓的散列冲突问题。

处理散列冲突常用的有两种方法:开发寻址法和链表法。

1、开放寻址法是当遇到散列值被使用的情况下就继续做数组中顺序寻找空闲位置,这样会有个缺点,就是如果散列冲突很多的时候,这种顺序去查找的时间就会变得特别长。而且还有个问题就是,如果不是哈希插入,而是哈希查找。一般来说是发现空闲位置前还没有发现目标元素则认为元素不在散列表。但是如果数组之前有元素被删除,相应位置出现空缺,就会错误的认为元素不在散列表。虽然可以用删除标记来表示,但这种方法还是不够方便,实际应用不多。

2、链表法,如果出现散列值相同的情况,就在相应位置建立1条链表,只要散列的分布足够均匀,不让大多数散列值相同,就不会让散列表退化。不然很多元素在一个散列值组成链表就容易变成单链表。

为了尽可能保证散列表的操作效率,一般情况下,我们会尽可能保证散列表中有一定比例的空闲槽位。我们用装载因子(load factor)来表示空位的多少。装载因子是目前已装入元素个数除以数组长度。装载因子越大,说明空闲位置越少,冲突越多,散列表的性能会下降。

一个能应对各种异常情况的工业级散列表也应该从上述三方面出发:

1、散列函数的设计,不能过于复杂,因为散列值会需要经常计算,复杂计算势必会影响到性能;另外散列函数的结果要尽可能随机且平均分布,减少散列冲突的可能。

2、装载因子可以作为散列表是否需要扩容的因素指标,当到底某个阈值后启动动态扩容,将旧散列表的数据迁移至新散列表。虽然这种操作经过均摊还是O(1),但是对于那一次来说,用户体验还是糟糕。可以将摊还真实的进行分割,旧数据迁移不是一次进行,而是伴随新数据存入同步进行,这样就对于每个用户来说时间都不会太明显。

3、根据使用场景选择合适的散列冲突解决方法。当数据量比较小、装载因子小的时候,适合采用开放寻址法。利用数组的优点,但更加消耗内容,因为散列冲突的影响更加严重。基于链表的散列冲突处理方法比较适合存储大对象、大数据量的散列表,而且,比起开放寻址法,它更加灵活,支持更多的优化策略,比如用红黑树代替链表。

散列表这种数据结构虽然支持非常高效的数据插入、删除、查找操作,但是散列表中的数据都是通过散列函数打乱之后无规律存储的。也就说,它无法支持按照某种顺序快速地遍历数据。如果希望按照顺序遍历散列表中的数据,那我们需要将散列表中的数据拷贝到数组中,然后排序,再遍历。那效率势必会很低。为了解决这个问题,我们将散列表和链表(或者跳表)结合在一起使用。可以看下图这个缓存系统的数据结构:

上图存在两种链表,一种是表示缓存更新时间的双向链表,另一种是散列表中用于解决散列冲突的链表。可以在O(1)时间里实现查找、删除、增加。散列表用于查找元素,删除和增加利用双向链表的指针也可以快速实现。

 

 

这篇关于数据结构与算法之美-散列表-极客时间笔记的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1017399

相关文章

Java controller接口出入参时间序列化转换操作方法(两种)

《Javacontroller接口出入参时间序列化转换操作方法(两种)》:本文主要介绍Javacontroller接口出入参时间序列化转换操作方法,本文给大家列举两种简单方法,感兴趣的朋友一起看... 目录方式一、使用注解方式二、统一配置场景:在controller编写的接口,在前后端交互过程中一般都会涉及

python处理带有时区的日期和时间数据

《python处理带有时区的日期和时间数据》这篇文章主要为大家详细介绍了如何在Python中使用pytz库处理时区信息,包括获取当前UTC时间,转换为特定时区等,有需要的小伙伴可以参考一下... 目录时区基本信息python datetime使用timezonepandas处理时区数据知识延展时区基本信息

Python的time模块一些常用功能(各种与时间相关的函数)

《Python的time模块一些常用功能(各种与时间相关的函数)》Python的time模块提供了各种与时间相关的函数,包括获取当前时间、处理时间间隔、执行时间测量等,:本文主要介绍Python的... 目录1. 获取当前时间2. 时间格式化3. 延时执行4. 时间戳运算5. 计算代码执行时间6. 转换为指

Java中字符串转时间与时间转字符串的操作详解

《Java中字符串转时间与时间转字符串的操作详解》Java的java.time包提供了强大的日期和时间处理功能,通过DateTimeFormatter可以轻松地在日期时间对象和字符串之间进行转换,下面... 目录一、字符串转时间(一)使用预定义格式(二)自定义格式二、时间转字符串(一)使用预定义格式(二)自

Python列表去重的4种核心方法与实战指南详解

《Python列表去重的4种核心方法与实战指南详解》在Python开发中,处理列表数据时经常需要去除重复元素,本文将详细介绍4种最实用的列表去重方法,有需要的小伙伴可以根据自己的需要进行选择... 目录方法1:集合(set)去重法(最快速)方法2:顺序遍历法(保持顺序)方法3:副本删除法(原地修改)方法4:

openCV中KNN算法的实现

《openCV中KNN算法的实现》KNN算法是一种简单且常用的分类算法,本文主要介绍了openCV中KNN算法的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录KNN算法流程使用OpenCV实现KNNOpenCV 是一个开源的跨平台计算机视觉库,它提供了各

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

利用Python快速搭建Markdown笔记发布系统

《利用Python快速搭建Markdown笔记发布系统》这篇文章主要为大家详细介绍了使用Python生态的成熟工具,在30分钟内搭建一个支持Markdown渲染、分类标签、全文搜索的私有化知识发布系统... 目录引言:为什么要自建知识博客一、技术选型:极简主义开发栈二、系统架构设计三、核心代码实现(分步解析

golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法

《golang获取当前时间、时间戳和时间字符串及它们之间的相互转换方法》:本文主要介绍golang获取当前时间、时间戳和时间字符串及它们之间的相互转换,本文通过实例代码给大家介绍的非常详细,感兴趣... 目录1、获取当前时间2、获取当前时间戳3、获取当前时间的字符串格式4、它们之间的相互转化上篇文章给大家介

Feign Client超时时间设置不生效的解决方法

《FeignClient超时时间设置不生效的解决方法》这篇文章主要为大家详细介绍了FeignClient超时时间设置不生效的原因与解决方法,具有一定的的参考价值,希望对大家有一定的帮助... 在使用Feign Client时,可以通过两种方式来设置超时时间:1.针对整个Feign Client设置超时时间