学习笔记
学习书目:《算法图解》- Aditya Bhargava


文章目录

  • 散列函数
    • 防止重复
  • 冲突
  • 性能
    • 填装因子

散列函数

散列函数是这样的一个函数,即无论你给它什么样的数据,它都还你一个数字。如果用专业术语来表达的话,我们可以说,散列函数“将输入映射到数字”。

我们可以认为散列函数输出的数字没啥规律,但是散列函数还是有一些必须要满足的要求的:

  • 它必须是一致的。例如,假设你输入apple时得到的是4,那么每次输入apple时,得到的都必须为4。
  • 它应将不同的输入映射到不同的数字。

散列函数将输入映射成数字,有啥用呢?

为了回答这个问题,我们构建一个空数组:

假如我有一个小商店,我要将商品的价格存储在这个数组中。

现在,我想先把apple的价格存入数组,则我将apple作为输入交给散列函数,并得到其输出为3,那么我们就把apple的价格存储到数组的索引3处;紧接着,我想将milk的价格存入数组,按照同样的步骤得到散列函数的输出0,则我们把milk的价格存储到数组的索引0处.不断重复这个步骤,直至数组填满:

如果,我现在想知道pen的价格,我不用在数组中查找,只需要将pen作为输入交给散列函数,散列函数告诉我pen的价格存储在数组的索引4处,那么我们就得到了pen的价格20.5

散列函数会准确的帮助我们找到商品价格的存储位置,我们不用自己去查找。之所以散列函数可以这样做,是因为以下几点:

  • 散列函数总是将同样的输入映射到相同的索引。
  • 散列函数将不同的输入映射到不同的索引。
  • 散列函数知道数组有多大,只返回有效的索引。

我们可以结合散列函数和数组创建一种被称为散列表的数据结构。散列表是一种包含额外逻辑的数据结构。数组和链表都被直接映射到内存,但散列表更复杂,它使用散列函数来确定元素的存储位置。在我们之后将学习的复杂数据结构中,散列表可能是最有用的,也被称为散列映射、映射、字典和关联数组。

Python提供的散列表实现为字典,我们可使用函数dict来创建散列表。

shop = dict()

创建散列表shop之后,我们在其中添加一些商品及其价格:

shop['apple'] = 3.5
shop['milk'] = 15.0
shop['pen'] = 20.5

我们看一下刚刚创建的字典:

In [67]: shop
Out[67]: {'apple': 3.5, 'milk': 15.0, 'pen': 20.5}

再利用字典查看一下pen的价格:

In [68]: shop['pen']
Out[68]: 20.5

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

防止重复

刚才我已经开了一个小商店,我每天要登记新进的商品,并把新商品的名称及其价格登记在我的散列表中。为了防止登记重复,我会在登记新商品之前,先检查一下散列表中是否已经登记过该商品,如果我发现已经登记了该商品(函数get返回该商品价格),那我就不登记它,如果没有登记过(函数get返回None),那我就登记它:

In [70]: print(shop.get('orange'))
NoneIn [71]: print(shop.get('pen'))
20.5

现在我们构造一个函数,来判断是否登记过某商品:

def check_pro(name, price):if shop.get(name) is None:shop[name] = priceprint('Register now')else:print('Item already exists')

控制台调用:

In [78]: check_pro('book', 30)
Register nowIn [79]: check_pro('book', 30)
Item already exists

冲突

在解释冲突之前,我想先举个例子方便理解。

假设我有一个数组,它有26个位置。我的散列函数规则很简单,它按照商品首字母的顺序分配商品价格在数组中的存储位置。这时,我们可能会提出疑问:如果我有两个商品book和bunny,它们的首字母都相同,那么我该怎么存储呢?如果我先存储了book的价格,它在数组的索引1处,那么当我存储bunny的价格时,该咋办呢?这种情况就叫做冲突:给两个键分配相同的位置。

处理冲突的方法有很多,最简单的就是下面这种:

如果两个键映射到了同一个位置,就在这个位置存储一个链表。

我们看到,book和bunny映射到了同一个位置,因此在这个位置存储一个链表。在查询apple的价格时,速度依然很快,但在查询bunny的价格时,速度要慢些:你必须在相应的链表中找到bunny.

我们可能会觉得这个链表很短,没什么大不了的。但是,如果我的商店进的大部分商品的商品名称都是以b开头的,那这个链表将会相当的长,我们用这个散列表的速度将会很慢,这是一个非常糟糕的状况。

这里总结了两点经验:

  • 散列函数很重要。前面的散列函数将所有的键都映射到一个位置,而最理想的情况是,散列函数将键均匀地映射到散列表的不同位置。
  • 如果散列表存储的链表很长,散列表的速度将急剧下降。然而,如果使用的散列函数很好,这些链表就不会很长。

散列函数很重要,好的散列函数能很少会导致冲突。

性能

在平均情况下,散列表执行各种操作(查找、插入、删除)的时间都为O(1)O(1)O(1). O(1)O(1)O(1)被称为常量时间,它并不意味着马上,而是说不管散列表多大,所需的时间都相同。

下面我们来比较线性时间、对数时间和常量时间:

我们看到上面第三幅图中,表示运行时间的曲线是水平的。这意味着平均情况下,无论散列表包含1个元素还是10亿个元素,从其中获取数据所需的时间都相同。

但在糟糕的情况下,散列表所有操作的运行时间都为O(n)O(n)O(n),即线性时间,这真的是很慢了。因此,为了避免冲突,需要有:

  • 较低的填装因子
  • 良好的散列函数

填装因子

散列表的填装因子很容易计算,即:散列表包含的元素数/位置总数。由此可知,填装因子度量的是散列表中有多少位置是空的。

散列表使用数组来存储数据,因此我们需要计算数组中被占用的位置数。例如下面的散列表的填装因子为2/5:

如果此时,我们的散列表只有50个位置,但是我要存储100件商品的价格,那么填装因子将为2.填装因子大于1则意味着,商品数量超过了数组的位置数。一旦填装因子开始增大,我们就需要在散列表中添加位置,这被称为调整长度。

例如,假设我的散列表有4个位置,我已经存储了3个商品价格,那么填装因子为3/4.此时,为了调整长度,我会先创建一个更长的新数组(通常将原数组增长1倍),这个新数组有8个位置,接下来,我需要使用函数hash将所有的元素都插入到这个新的散列表中,那么这个新散列表的填装因子为3/8,比原来低得多。填装因子越小,发送冲突可能性就越小。一个不错的经验规则是:一旦填装因子大于0.7,就调整散列表的长度。

小白的算法初识课堂(part5)--散列表相关推荐

  1. 小白的算法初识课堂(part8)--贪婪算法

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 文章目录 教室调度问题 集合覆盖问题 近似算法 代码实现 NP完全问题 教室调度问题 假如我是一个学校的校长,我们学校有 ...

  2. 小白的算法初识课堂(part7)--狄克斯特拉算法

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 文章目录 狄克斯特拉算法 具体步骤实现 术语 跳蚤市场 具体步骤实现 负权边 python实现 狄克斯特拉算法 在上一个 ...

  3. 小白的算法初识课堂(part6)--广度优先搜索

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 文章目录 图简介 图是啥 广度优先搜索 寻找最短路径 队列 实现图 实现算法 运行时间 图简介 今天是五一,假如我要从家 ...

  4. 小白的算法初识课堂(part9)--SHA及Simhash算法

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 安全散列算法(SHA) 在学SHA算法之前,我们先回顾一下前几个Blog所学的散列函数. 散列函数是这样的一个函数,即无 ...

  5. 小白的算法初识课堂(part4)--快速排序

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 分而治之 在这里,我想通过2个例子介绍一种著名的递归式问题解决方法–分而治之(D&C) 分蛋糕 假如,我要分一块 ...

  6. 小白的算法初识课堂(part2)--选择排序

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 文章目录 选择排序 内存工作原理 数组 链表 读取 索引 插入 删除 是常见的数组和链表操作的运行时间 选择排序 pyt ...

  7. 小白的算法初识课堂(part1)--二分查找法

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 二分查找法 算法是一组完成任务的指令,任何代码片段都可视为算法.二分查找是一种算法,其输入是一个有序的元素列表(必须有序 ...

  8. 小白的算法初识课堂(part3)--递归

    学习笔记 学习书目:<算法图解>- Aditya Bhargava 文章目录 递归 基线条件和递归条件 栈 调用栈 递归调用栈 递归 首先,我们看一段代码: def print_num(m ...

  9. 算法设计与分析——散列表/哈希表(Hash Table):直接寻址表

    分类目录:<算法设计与分析>总目录 相关文章: ·散列表/哈希表(Hash Table)(一):基础知识 ·散列表/哈希表(Hash Table)(二):直接寻址表 ·散列表/哈希表(Ha ...

最新文章

  1. 角度编码器 ST-3806-15-RS
  2. Windows存储串口数据至txt文件中的教程及代码
  3. VTK:相互作用之SelectAVertex
  4. java jar包示例_Java包getImplementationVersion()方法和示例
  5. SQL SERVER 高级数据库脚本编程
  6. Python中李群SO(3)与李代数so(3)之间指数映射与实现源码
  7. 黑马程序员——程序结构
  8. 【java】简介(一)
  9. web前端开发初学者十问集锦(5)
  10. 各种开源项目/库/工具介绍
  11. 苏州真不能成为一线城市?
  12. 新版白话空间统计(3):空间分布模式
  13. 将selinux=enforing设置好过后,重启Linux管理员账号不能登陆
  14. 列主元消去法例题详解_列主元消去法
  15. Google TV 来了
  16. 如何搭建vue项目,完整搭建vue项目
  17. 利用 ajax 上传图片 删除图片 (Spring Boot)
  18. Hash-based Shuffle内幕彻底解密
  19. Linux驱动 | OLED显示模块驱动(SPI)
  20. cesium将默认底图设置为不显示

热门文章

  1. 远程连接MySql数据库
  2. hdu1068 Girls and Boys --- 最大独立集
  3. Could not load java.net.BindException错误解决
  4. How To Make JMeter Behave More Like A Real Browser
  5. ansible基本模块使用
  6. LoaderManager使用详解(一)---没有Loader之前的世界
  7. cocos2d-iphone源码分析(2):Director
  8. 在对的时间 遇见对的人 是一种幸福
  9. RedHat7安装Tomcat
  10. mysql5.7.23手动配置安装windows版