Python 哈希表的实现——字典

哈喽大家好,我是咸鱼

接触过 Python 的小伙伴应该对【字典】这一数据类型都了解吧

虽然 Python 没有显式名称为“哈希表”的内置数据结构,但是字典是哈希表实现的数据结构

在 Python 中,字典的键(key)被哈希,哈希值决定了键对应的值(value)在字典底层数据存储中的位置

那么今天我们就来看看哈希表的原理以及如何实现一个简易版的 Python 哈希表

ps:文中提到的 Python 指的是 CPyhton 实现

何为哈希表?

哈希表(hash table)通常是基于“键-值对”存储数据的数据结构

哈希表的键(key)通过哈希函数转换为哈希值(hash value),这个哈希值决定了数据在数组中的位置。这种设计使得数据检索变得非常快

举个例子,下面有一组键值对数据,其中歌手姓名是 key,歌名是 value

+------------------------------+
|   Key        |   Value       |
+------------------------------+
| Kanye        | Come to life  |
| XXXtentacion | Moonlight     |
| J.cole       | All My Life   |
| Lil wanye    | Mona Lisa     |
| Juice WRLD   | Come & Go     |
+------------------------------+

如果我们想要将这些键值对存储在哈希表中,首先需要将键的值转换成哈希表的数组的索引,这时候就需要用到哈希函数了

哈希函数是哈希表实现的主要关键,它能够处理键然后返回存放数据的哈希表中对应的索引

一个好的哈希函数能够在数组中均匀地分布键,尽量避免哈希冲突(两个键返回了相同的索引)

在这里插入图片描述
哈希函数是如何处理键的,这里我们创建一个简易的哈希函数来模拟一下(实际上哈希函数要比这复杂得多)

def simple_hash(key, size):return ord(key[0]) % size

这个简易版哈希函数将歌手名(即 key)首字母的 ASCII 值与哈希表大小取余,得出来的值就是歌名(value)在哈希表中的索引

那这个简易版哈希函数有什么问题呢?聪明的你一眼就看出来了:容易出现碰撞。因为不同的键的首字母有可能是一样的,就意味着返回的索引也是一样的

例如我们假设哈希表的大小为 10 ,我们以上面的歌手名作为键然后执行 simple_hash(key, 10) 得到索引在这里插入图片描述
可以看到,由于Juice WRLDJ.cole 的首字母都一样,哈希函数返回了相同的索引,这里就发生了哈希碰撞

虽然几乎不可能完全避免任何大量数据的碰撞,但一个好的哈希函数加上一个适当大小的哈希表将减少碰撞的机会

当出现哈希碰撞时,可以使用不同的方法(例如开放寻址法)来解决碰撞

应该设计健壮的哈希函数来尽量避免哈希碰撞

我们再来看其他的键,Kanye 通过 simple_hash() 函数返回 index 5,这意味着我们可以在索引 5 (哈希表的第六个元素)上找到 其键 Kanye 和值Come to life
在这里插入图片描述
哈希表优点

在哈希表中,是根据哈希值(即索引)来寻找数据,所以可以快速定位到数据在哈希表中的位置,使得检索、插入和删除操作具有常数时间复杂度 O(1) 的性能

与其他数据结构相比,哈希表因其效率而脱颖而出

不但如此,哈希表可以存储不同类型的键值对,还可以动态调整自身大小

Python 中的哈希表实现

在 Python 中有一个内置的数据结构,它实现了哈希表的功能,称为字典

Python 字典(dictionary,dict)是一种无序的、可变的集合(collections),它的元素以 “键值对(key-value)”的形式存储

字典中的 key 是唯一且不可变的,这意味着它们一旦设置就无法更改

my_dict = {"Kanye": "Come to life", "XXXtentacion": "Moonlight", "J.cole": "All My Life"}

在底层,Python 的字典以哈希表的形式运行,当我们创建字典并添加键值对时,Python 会将哈希函数作用于键,从而生成哈希值,接着哈希值决定对应的值将存储在内存的哪个位置中

所以当你想要检索值时,Python 就会对键进行哈希,从而快速引导 Python 找到值的存储位置,而无需考虑字典的大小

my_dict = {}
my_dict["Kanye"] = "Come to life" # 哈希函数决定了 Come to life" 在内存中的位置
print(my_dict["Alice"]) # "Come to life" 

可以看到,我们通过方括号[key]来访问键对应的值,如果键不存在,则会报错

print(my_dict["Kanye"])  # "Come to life" # Raises KeyError: "Drake"
print(my_dict["Drake"])

为了避免该报错,我们可以使用字典内置的 get() 方法,如果键不存在则返回默认值

print(my_dict.get('Drake', "Unknown")) # Unknown

在 python 中实现哈希表

首先我们定义一个 HashTable 类,表示一个哈希表数据结构

class HashTable:def __init__(self, size):self.size = sizeself.table = [None]*sizedef _hash(self, key):return ord(key[0]) % self.size

在构造函数 __init__() 中:

  • size 表示哈希表的大小
  • table是一个长度为 size 的数组,被用作哈希表的存储结构。初始化时,数组的所有元素都被设为 None,表示哈希表初始时不含任何数据

在内部函数 _hash() 中,用于计算给定 key 的哈希值。它采用给定键 key 的第一个字符的 ASCII 值,并使用取余运算 % 将其映射到哈希表的索引范围内,以便确定键在哈希表中的存储位置。

然后我们接着在 HashTable 类中添加对键值对的增删查方法

class HashTable:def __init__(self, size):self.size = sizeself.table = [None]*sizedef _hash(self, key):return ord(key[0]) % self.sizedef set(self, key, value):hash_index = self._hash(key)self.table[hash_index] = (key, value)def get(self, key):hash_index = self._hash(key)if self.table[hash_index] is not None:return self.table[hash_index][1]raise KeyError(f'Key {key} not found')def remove(self, key):hash_index = self._hash(key)if self.table[hash_index] is not None:self.table[hash_index] = Noneelse:raise KeyError(f'Key {key} not found')

其中,set() 方法将键值对添加到表中,而 get() 该方法则通过其键检索值。该 remove() 方法从哈希表中删除键值对

现在,我们可以创建一个哈希表并使用它来存储和检索数据:

# 创建哈希表
hash_table = HashTable(10)# 添加键值对
hash_table.set('Kanye', 'Come to life')
hash_table.set('XXXtentacion', 'Moonlight')# 获取值
print(hash_table.get('XXXtentacion'))  # Outputs: 'Moonlight'# 删除键值对
hash_table.remove('XXXtentacion')# 报错: KeyError: 'Key XXXtentacion not found'
print(hash_table.get('XXXtentacion'))

前面我们提到过,哈希碰撞是使用哈希表时不可避免的一部分,既然 Python 字典是哈希表的实现,所以也需要相应的方法来处理哈希碰撞

在 Python 的哈希表实现中,为了避免哈希冲突,通常会使用开放寻址法的变体之一,称为“线性探测”(Linear Probing)

当在字典中发生哈希冲突时,Python 会使用线性探测,即从哈希冲突的位置开始,依次往后查找下一个可用的插槽(空槽),直到找到一个空的插槽来存储要插入的键值对。

这种方法简单直接,可以减少哈希冲突的次数。但是,它可能会导致“聚集”(Clustering)问题,即一旦哈希表中形成了一片连续的已被占用的位置,新元素可能会被迫放入这片区域,导致哈希表性能下降

为了缓解聚集问题,假若当哈希表中存放的键值对超过哈希表长度的三分之二时(即装载率超过66%时),哈希表会自动扩容

最后总结一下:

  • 在哈希表中,是根据哈希值(即索引)来寻找数据,所以可以快速定位到数据在哈希表中的位置
  • Python 的字典以哈希表的形式运行,当我们创建字典并添加键值对时,Python 会将哈希函数作用于键,从而生成哈希值,接着哈希值决定对应的值将存储在内存的哪个位置中
  • Python 通常会使用线性探测法来解决哈希冲突问题

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

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

相关文章

出于隐私和安全的考虑,有时需要从谷歌删除你的个人数据,有两种方法

如果你是公众人物、企业或拥有个人品牌的人,那么拥有在线形象很重要。然而,你可能会发现,通过谷歌搜索,陌生人可以获得你的个人信息,如联系方式、地址和财务信息,这会让你感到不安。 幸运的是,…

系统频繁崩溃,如何考虑系统的稳定性和可扩展性?

最近网传互联网应用信息系统频繁崩溃,语雀崩完淘宝崩,淘宝崩完滴滴崩,随着业务的发展和技术的进步,对于信息系统的要求也越来越高。信息应用系统为了满足不断增长的用户和业务需求,提高系统的稳定性和扩展性至关重要。…

短 URL 生成器设计:百亿短 URL 怎样做到无冲突?

Java全能学习面试指南:https://javaxiaobear.cn 我们先来看看,当高并发遇到海量数据处理时的架构。在社交媒体上,人们经常需要分享一些 URL,但是有些 URL 可能会很长,比如: https://time.geekbang.org/hyb…

pytest分布式执行(pytest-xdist)

前言 平常我们手工测试用例非常多时,比如有1千条用例,假设每个用例执行需要1分钟。如果一个测试人员执行需要1000分钟才能执行完,当项目非常紧急的时候,我们会用测试人力成本换取时间成本,这个时候多找个小伙伴把任务…

MySQL根据binlog恢复数据

简介 本文介绍了使用mysqlbinlog导出数据,根据binlog恢复数据,和导出数据时报需要super权限的解决方法。 环境 MySQL: 5.7.40 说明 MySQL的binlog是数据库服务器在运行过程中产生的日志文件,记录了数据库增删改的操作,可用于恢复和…

重排链表,剑指offerII 26,力扣 120

目录 力扣题目地址: 题目: 那我们直接看题解吧: 解题方法: 难度分析: 审题目事例提示: 解题分析: 解题思路: 解题补充: 力扣题目地址: 143. 重排链表 - 力扣&…

基于Django+Tensorflow卷积神经网络鸟类识别系统

欢迎大家点赞、收藏、关注、评论啦 ,由于篇幅有限,只展示了部分核心代码。 文章目录 一项目简介系统概述系统功能核心技术系统架构系统优势 二、功能三、系统四. 总结  总结 一项目简介 介绍一个基于DjangoTensorflow卷积神经网络鸟类识别系统是一个非…

多目标水母搜索算法(MOJS)求解微电网优化MATLAB

一、微网系统运行优化模型 微电网优化模型介绍: 微电网多目标优化调度模型简介_IT猿手的博客-CSDN博客 二、多目标水母搜索算法MOJS 多目标水母搜索算法(Multi-Objective Jellyfish Search algorithm,MOJS)由Jui-Sheng Chou等…

Verilog 入门(一)(Verilog 简介)

文章目录 什么是 Verilog HDL?Verilog 主要能力模块时延数据流描述方式 什么是 Verilog HDL? Verilog HDL是一种硬件描述语言,用于从算法级、门级到开关级的多种抽象设计层次的数字系统建模。被建模的数字系统对象的复杂性可以介于简单的门和…

景联文科技加入中国人工智能产业联盟(AIIA)数据委员会

近日,景联文科技加入中国人工智能产业联盟(AIIA)数据委员会,成为委员会成员单位。 中国人工智能产业发展联盟(简称AIIA)是在国家发改委、科技部、工信部、网信办指导下,由中国信息通信研究院等单…

基于springboot家具商城系统

基于springboot家具商城系统 摘要 这个Spring Boot家具商城系统旨在通过现代化的Web应用程序技术提供一个全功能的电子商务平台。系统的核心实体是家具商品,通过RESTful API控制器提供对商品的增删改查等操作。这个系统使用了Spring Boot框架,借助其简便…

Mac单独修改应用语言

方法1: 方法2: defaults write com.microsoft.Excel AppleLanguages ("zh-cn") defaults write com.microsoft.Word AppleLanguages ("zh-cn")参考:https://www.zhihu.com/question/24976020

红队攻防实战系列一之Cobalt Strike

他日若遂凌云志,敢笑黄巢不丈夫 本文首发于先知社区,原创作者即是本人 前言 在红队攻防中,需要我们拥有综合能力,不仅仅是web渗透的漏洞挖掘与利用,边界突破的方式有很多种,当然这需要我们拥有很强的意识…

【LeetCode】101. 对称二叉树

101. 对称二叉树 难度:简单 题目 给你一个二叉树的根节点 root , 检查它是否轴对称。 示例 1: 输入:root [1,2,2,3,4,4,3] 输出:true示例 2: 输入:root [1,2,2,null,3,null,3] 输出&#…

图书管理系统源码,图书管理系统开发,图书借阅系统源码配置和运行图解源码已附加

目录 配置简介和软件条件 数据库附件配置 vs应用程序web.config配置数据库链接字符串 数据库文件脚本代码 配置简介和软件条件 所需要的软件是Vs2017以上数据库是Sqlserver2012以上,如果数据库附件不了可以使用数据库脚本附件数据库脚本会在文章末尾写出来。可以…

ArkTS-页面转场动画

页面转场动画 在全局pageTransition方法内配置页面入场和页面退场时的自定义转场动效 两个页面间发生跳转,一个页面消失,另一个页面出现,这时可以配置各自页面的页面转场参数实现自定义的页面转场效果。页面转场效果写在pageTransition函数中…

【Python】获取ip

要使用Python获取IP地址,可以使用socket库中的gethostname()函数和gethostbyname()函数。 import socketdef get_ip_address():hostname socket.gethostname()ip_address socket.gethostbyname(hostname)return ip_addressip get_ip_address() print("IP地…

【BUG合集】(一)①数据库存1/0,请求结果返回true和false;②sql查数据库能查,但mybatis查为空;③data64图片存储为异常;

前言 最近,在工作上接手的任务中,各种 bug 问题出现,在解决的同时也可以记录一下。因此,觉得可以出个记录 bug 合集。方便后来者碰到类似情况,可以作为一个参考进行解决。 文章题目就包含当前文章内容中所遇到的三个 b…

用行云管家实现IT统一运维管理,提高运维效率

随着公司业务的不断壮大,需要用到的IT系统也越来越多,使用起来耗时耗力。因此实现IT统一运维管理已成为提高运维效率、降低成本、优化资源配置的重要途径。这里我们小编告诉您,用行云管家实现IT统一运维管理,提高运维效率&#xf…

目标检测——R-CNN算法解读

论文:Rich feature hierarchies for accurate object detection and semantic segmentation 作者:Ross Girshick, Jeff Donahue, Trevor Darrell, Jitendra Malik 链接:https://arxiv.org/abs/1311.2524 代码:http://www.cs.berke…