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

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时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

Python中DataFrame转列表的最全指南

《Python中DataFrame转列表的最全指南》在Python数据分析中,Pandas的DataFrame是最常用的数据结构之一,本文将为你详解5种主流DataFrame转换为列表的方法,大家可以... 目录引言一、基础转换方法解析1. tolist()直接转换法2. values.tolist()矩阵

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

python展开嵌套列表的多种方法

《python展开嵌套列表的多种方法》本文主要介绍了python展开嵌套列表的多种方法,包括for循环、列表推导式和sum函数三种方法,具有一定的参考价值,感兴趣的可以了解一下... 目录一、嵌套列表格式二、嵌套列表展开方法(一)for循环(1)for循环+append()(2)for循环+pyPhWiFd

Python容器类型之列表/字典/元组/集合方式

《Python容器类型之列表/字典/元组/集合方式》:本文主要介绍Python容器类型之列表/字典/元组/集合方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1. 列表(List) - 有序可变序列1.1 基本特性1.2 核心操作1.3 应用场景2. 字典(D

Python如何获取域名的SSL证书信息和到期时间

《Python如何获取域名的SSL证书信息和到期时间》在当今互联网时代,SSL证书的重要性不言而喻,它不仅为用户提供了安全的连接,还能提高网站的搜索引擎排名,那我们怎么才能通过Python获取域名的S... 目录了解SSL证书的基本概念使用python库来抓取SSL证书信息安装必要的库编写获取SSL证书信息

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

MySQL 日期时间格式化函数 DATE_FORMAT() 的使用示例详解

《MySQL日期时间格式化函数DATE_FORMAT()的使用示例详解》`DATE_FORMAT()`是MySQL中用于格式化日期时间的函数,本文详细介绍了其语法、格式化字符串的含义以及常见日期... 目录一、DATE_FORMAT()语法二、格式化字符串详解三、常见日期时间格式组合四、业务场景五、总结一、