算法图解part5:散列表

2023-11-01

1.散列(hashing)函数

散列函数也称为散列映射、映射、字典、关联数组、哈希函数等。

概念:
散列的概念属于查找,它不以关键字的比较为基本操作,采用直接寻址技术。在理想情况下,查找的期望时间为O(1)。
hash函数就是把任意长的输入字符串变化成固定长的输出字符串的一种函数。输出字符串的长度称为hash函数的位数。
散列(Hashing)通过散列函数将要检索的项与索引(散列,散列值)关联起来,生成一种便于搜索的数据结构(散列表)。

算法图解介绍:
散列函数是这样的函数,即无论你给它什么数据,它都还你一个数字。即散列函数“将输入映射到数字”。
散列函数必须满足一些要求:

  1. 它必须是一致的。
  2. 它应将不同的输入映射到不同的数字。

例如1,假设你输入apple时得到的是4,那么每次输入apple时,得到的都必须为4。
例如2,如果一个散列函数不管输入是什么都返回1,那它就不是好的散列函数。最理想的情况是 将不同的输入映射到不同的数字。

以商品价格的图例说明散列表的结构
假设apple、orange 价格如下表所示

----- apple orange
Price 1.45¥/kg 1.2¥/kg

创建一个空数组:(None为数组初始值,这里仅为了记录方便写成None)

0 1 2 3
None None None None

将 apple 输入散列函数,输出数字为0,并把它作为数组索引,将价格存入数组。
同样的,将 orange 输入 散列函数,输出数字为2,并把它作为数组索引,将价格存入数组。

0 1 2 3
1.49 None 1.2 None

现在我们查询apple的价格,将apple 输入 散列函数输出索引值0,直接查询对应的数组值,即为apple的价格。

将商品名称作为输入给散列函数,散列函数给出对应的索引值,将商品对应的价格放入索引值对应的数组位置。可以在O(1)的时间复杂度下找到相应的结果,主要得益于以下几点:

  • ①散列函数总是将同样的输入映射到相同的索引。
  • ②散列函数将不同的输入映射到不同的索引。(这里实际是一种理想的状态,见下文 冲突 部分)
  • ③散列函数知道数组有多大,只返回有效的索引。

散列表是一种包含额外逻辑的数据结果(散列函数),与数组和链表被直接映射到内存不同

python提供函数 d i c t ( ) dict() dict()创建散列表(键值对)代码如下:

book  = dict()  
book["apple"] = 0.67
book["milk"] = 1.49
book["avocado"] = 1.60

print(book)
print(book["milk"])

运行结果:

{‘apple’: 0.67, ‘milk’: 1.49, ‘avocado’: 1.6}
1.49

散列表由键和值组成。在前面的散列表book中,键为商品名,值为商品价格。散列表将键映射到值。

2.散列表的应用

  • 1.用于查找(类似手机的电话薄功能、DNS解析等)
  • 2.防止重复(投票等)
  • 3.用于缓存(站点数据访问等)
    还有更多的列举…

2.1将散列表用于查找

假设你要创建一个电话簿,将姓名映射到电话号码。该电话簿需要提供如下功能:

1.添加联系人及其电话号码。
2.通过输入联系人来获悉其电话号码。

下面我们来使用散列表进行对电话簿的创建映射和查找。python代码:

phone_book = dict()
phone_book["wang"] = 13053205320
phone_book["shuai"] = 13956785678

print(phone_book["wang"])

运行结果:

13053205320

书中例子提到 DNS解析(DNS resolution):将网址映射为IP地址。

2.2防止重复

假如你负责管理一个投票站,每个人只能投一票,如何避免重复投票呢?

voted = dict()

def checkname(name):
    if voted.get(name):
        print("kick them out!")
    else:
        voted[name] = True
        print("let them vote!")

checkname("wang")
checkname("shuai")
checkname("wang")

运行结果:

let them vote!
let them vote!
kick them out!

2.3用于缓存

缓存是一种常用的加速方式,所有大型网站都使用缓存,而缓存的数据则存储在散列表中。

缓存的优点:

  • 用户能够更快地看到网页。
  • 服务器需要做的工作很少。
cache = {}

def get_page(url): 
    if cache.get(url):
        return cache[url] # 返回缓存的数据
    else:
        data = get_data_from_server(url)
        cache[url] = data # 先将数据保存到缓存中
        return data

说明:仅当URL不在缓存中时,让服务器做这些处理,并将处理生成的数据存储到缓存中,再返回它。这样,当下次有人请求该URL时,你就可以直接发送缓存中的数据,而不用再让服务器进行处理,耗费资源。

3.冲突

给两个键分配的位置相同就是冲突,如何处理?

冲突例子如下:

  • 超市使用散列函数来存储商品价格。现有数组 a[26],存储对应A~Z字母开头的商品价格。
    (如apple输入散列,输出0;banana输入散列,输出为1)

在这里插入图片描述

  • 如果有两个以A字母开头的商品,由于散列函数输出的索引值均为0,故出现冲突:
    在这里插入图片描述
  • 可取商品的一个办法是,如果两个键映射到了同一个位置,就在这个位置储存一个链表。
    在这里插入图片描述
  • 糟糕的情况下,链表太长,而其它的分配的空间又浪费掉了。
    在这里插入图片描述
    因此,散列函数的选择很重要

4.性能

在平均情况下,散列表执行各种操作的时间都为O(1),O(1)被称为常量时间。

------ 散列表(平均情况) 散列表(最糟情况) 数组 链表
查找 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1) O ( n ) O(n) O(n)
插入 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)
删除 O ( 1 ) O(1) O(1) O ( n ) O(n) O(n) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)

PS:散列表一般为平均情况,最糟情况较少,故默认为 O ( 1 ) O(1) O(1)
而要避免上述呈现的最糟的情况,就要避免冲突,那么就需要做好下面两点:
较低的填装因子与良好的散列函数

4.1填装因子

填装因子=散列表包含的元素数 / 位置总数

填装因子 >1 说明数量超过了数组的位置数。此时需要调整长度。
填装因子越低,发生冲突的可能性越小,散列表性能越高。一般情况下,填装因子大于0.7,就需要调整长度。

4.2良好的散列函数

良好的散列函数让数组中的值呈均匀分布。糟糕的散列函数让值扎堆,导致大量的冲突。
拓展:SHA函数 可将其作为散列函数。

5.总结

  • 不用自己实现散列表,大多语言提供了实现。(但是原理很重要)
  • 可以结合散列函数和数组来创建散列表。
  • 为避免冲突,应使用最大限度的减少冲突的散列函数。
  • 散列表的查找、插入和删除的速度都很快。
  • 散列表适合用于模拟映射关系。
  • 一旦填装因子大于0.7,应调整散列表的长度。
  • 散列表可用于缓存数据等(例如,在web服务器上)。
  • 散列表适合用于重复

6.参考资料

《算法图解》第五章

此部分学习算法内容已上传github:https://github.com/ShuaiWang-Code/Algorithm/tree/master/Chapter5

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

算法图解part5:散列表 的相关文章

随机推荐

  • Django pycharm控制台日志模块报错和日志配置

    一 windows 环境pycharm的控制台日志报错 背景 在windows环境下 当日志文件大小超过配置设定的大小时 有时候会报错 文件另一个程序正在使用此文件 进程无法访问 原因分析 原因是我打开了多个debug控制台 当我首先打开下
  • D365 CRM 在视图中添加自定义图标

    此示例在客户视图中显示自定义图标 我在客户实体上添加了一个自定义字段 客户等级 选项集有重点 Value 1 和普通 Value 2 根据选项集的不同显示不同的图标 建议使用图标大小为 16x16 像素 较大的图像将按比例缩小 图片格式可以
  • 希腊棺材之谜——复盘

    文章目录 梗概 推导 伪解答 虽然花费6 8小时来看小说 是一件很奢侈的事情 但是再荒诞的事情终归有它背后的逻辑链条 这正如Ellery所坚持的那样 逻辑为王 希腊棺材之谜是Ellery Queen首次展露头角 因此作者特地给他安排了3次伪
  • 启动fiddler导致浏览器无法上网的解决方法

    文章来源 https blog csdn net u010404991 article details 79071894 1 开发fiddler 进入Tools gt Fiddler Tools 按照如图3部配置 即可实现无法上网的问题 2
  • Ubuntu 开机自动运行命令

    Ubuntu开机自动运行自定义的命令 可以采用两种方式 第一种shell脚本方式 1 进入 etc init d 目录 root Ubuntu cd etc init d 2 新建一个自定义名称的sh脚本 这里以 xxx 名称为例建立一个
  • 服务器dbback文件夹,怎么让SQL 2000定时复制备份数据库到局域网中的指定电脑上? - SQL Server论坛 - 51CTO技术论坛_中国领先的IT技术社区...

    如题 这是我在网上找的JOB脚本 试用只能在本地盘符间复制有效 局域网中的共享失效 系统环境 WIN2K3 SQL 2000 SP4 网络环境 SQL服务器 192 168 1 2 备份服务器 192 168 1 3 在备份服务器上新建共享
  • Java 动态代理和静态代理 详解(结合代码实列)

    文章目录 Java 动态代理和静态代理的区别 下面是一个结合代码示例 运行上述代码 输出如下 总结 Java 动态代理和静态代理的区别 动态代理和静态代理是两种不同的代理模式 它们在代理对象的创建和使用方式上有所不同 静态代理 静态代理是在
  • 【期末大作业】语料库--分类、聚类、关系提取

    一 课程要求以及说明 1 使用分类 聚类 关系提取对数据集进行相关的分析 在使用数据集之前 需要用到数据预处理 2 可以在以下方法中选择一种聚类和一种分类技术 聚类 无监督学习 C1 network analysis 网络分析 C2 k m
  • lnmp环境搭建的详细过程(ubuntu22)

    软件及软件版本的信息如下 nginx 1 18 0 mysql 8 0 32 php 8 1 备注 我的ubuntu环境是Windows下基于WSL2的Ubuntu 想看怎么实现的可以看这个文章 https blog csdn net qq
  • CVE-2021-4034 Polkit

    CVE 2021 4034 Polkit 0x01 漏洞介绍 Polkit是一个应用程序级别的工具集 通过定义和审核权限规则 实现不同优先级进程间的通讯 控制决策集中在统一的框架之中 决定低优先级进程是否有权访问高优先级进程 另外Polki
  • 熔断,降级,限流的区别

    熔断 降级 限流 熔断 Circuit Breaking 限流 Rate Limiting 降级 Fallback 熔断 Circuit Breaking 一种用于处理依赖服务故障的策略 当依赖服务出现故障或超时 熔断机制会迅速中断对该服务
  • 【测试设计】使用jenkins 插件Allure生成自动化测试报告

    前言 以前做自动化测试的时候一直用的HTMLTestRunner来生成测试报告 后来也尝试过用Python的PyH模块自己构建测试报告 在后来看到了RobotFramework的测试报告 感觉之前用的测试报告都太简陋 它才是测试报告应该有的
  • 基于SSM + MySql + LayUI的图书管理系统

    点击下载 图书管理系统 文件大小 72M 操作系统 Windows10旗舰版 数据库 MySQL5 7 BookManageSystemLayui src main resources sql 开发工具 idea2021 JDK8 Mave
  • web前端进阶大厂面试资料合集

    最近整理了下web前端面试的资料 包含了web前端 数据结构和算法 计算机基础 版本控制工具 经验分享 视频课程和面试书籍等资料 还有比这更全的没有 废话不多说 直接上干货 欢迎收藏 不用客气 前端 面试官求你别再问我hook了 程序员必须
  • Wing IDE安装与破解方法

    WingIDE的licese破解方法 1 安装WingIDE成功后启动 激活时输入license id CN123 12345 12345 12345 2 点击Continue后弹框 拷贝框中的request code 将其放入脚本中的Re
  • 获取微信小程序登录code和获取手机号code

    index ts 获取应用实例 const app getApp
  • QT自定义槽方法

    本文简介点击窗体上的按钮后 改变窗体标题的方法 在窗体上放置好按钮之后 有以下三步操作 声明 gt 实现 gt 连接 1 声明 在头文件mainwindow h中声明一个槽 private slots void changeTitleSlo
  • pychar常用快捷键及转义符号

    Alt 1 影藏和显示项目列表 Ctrl shift F10 运行代码 Ctrl shift F4 关闭Tab 终端运行面板 Ctrl 注释代码 取消注释 Ctrl d 复制行 Ctrl L 格式化代码 PEP8编码格式 shift Alt
  • CGridCtrl(集成了打印预览与合并单元格)

    ucogrid src zip
  • 算法图解part5:散列表

    算法图解part5 散列表 1 散列 hashing 函数 2 散列表的应用 2 1将散列表用于查找 2 2防止重复 2 3用于缓存 3 冲突 4 性能 4 1填装因子 4 2良好的散列函数 5 总结 6 参考资料 1 散列 hashing