【算法】算法图解笔记_散列表

线性查找的时间复杂度O(n),二分查找为O(logn),有没有时间复杂度为O(1)的查找吗? 固然有,这就是散列表。python

散列函数

散列函数“将输入映射到数字”。其必须知足一些要求。数组

  1. 它必须是一致的。对于一样的输入,输出必须是同样的。
  2. 最理想的状况是,将不一样的输入映射到不一样的数字。这样,不一样的输入就会映射到不一样的位置。

这样就能够使用散列函数将输入映射到数组的不一样位置,从而获得一个简单的散列表(hash table)。散列表是目前该书介绍的第一种包含额外逻辑的数据结构。数组和链表都被直接映射到内存,但散列表更复杂,它使用散列函数来肯定元素的存储位置。散列表由组成,将键映射到值。缓存

同时散列表也被称为散列映射映射字典关联数组。好比,Python提供的散列表实现为字典。Python内置字典的用法:服务器

>>> book = dict() # 建立空字典
>>> book["apple"] = 0.67    # 添加键值对
>>> book["milk"] = 1.49        # 添加键值对
>>> book["avocado"] = 1.49    # 添加键值对
>>> print(book)            # 打印当前散列表
{'apple': 0.67, 'milk': 1.49, 'avocado': 1.49}
>>> print(book["avocado"])    # 散列表使用键查找值
1.49

应用案例

将散列表用于查找

经过键快速查找其关联的值。数据结构

好比,电话簿
姓名为键,电话号码为值app

DNS解析(DNS resolution)
域名为键,IP地址为值函数

防止重复

好比,投票限制每人只能投一票。能够将某人的信息(如,姓名、IP等)做为键存到散列表内,每次用户投票前,先看看以前有没有投过,没投的话容许投票,并将信息存到散列表内,若是投过票,则不容许再投。性能

将散列表用做缓存

缓存是一种经常使用的加速方式,全部大型网站都使用缓存,而缓存的数据则存储在散列表中。缓存的工做原理:网站将数据记住,而再也不从新计算,这样的好处是减小响应的时间,同时也节省了服务器的计算资源。网站

当访问网站的页面时,它首先检查散列表中是否存储了该页面。仅当URL不在缓存中时,你才让服务器作些处理,并将处理生成的数据存储到缓存中,再返回它。这样,当下次有人请求该URL时,你就能够直接发送缓存中的数据,而不用再让服务器进行处理了。spa

冲突(collision)

最理想的状况是,散列函数将不一样的输入映射到不一样的数字,但几乎不可能编写出这样的散列函数。
冲突:给两个键分配的位置相同。

处理冲突的方式不少,最简单的办法以下:若是两个键映射到了同一个位置,就在这个位置存储一个链表。
图片描述

这种处理方式也有可能出现最糟的状况,如,除第一个位置外,整个散列表都是空的,而第一个位置包含一个很长的列表,这时查找速度等同于链表的查找速度,会很慢。

好的散列函数不多致使冲突,这样会将键均匀地映射包散列表的不一样位置,从而使链表不会太长。

性能

在平均状况下,散列表执行各类操做的时间都为O(1)。O(1)被称为常量时间。常量时间并不意味着立刻,而是说无论散列表多大,所需的时间都相同。在最糟状况下,散列表全部操做的运行时间都为O(n)——线性时间。

要避免冲突,须要有:

  • 较低的填装因子;

填装因子 = 散列表包含的元素数 / 位置总数
填装因子度量的是散列表中有多少位置是空的。

一旦填装因子开始增大,你就须要在散列表中添加位置,这被称为调整长度(resizing) 。调整长度须要从新建立新的存储空间,而后使用散列函数将全部的元素都插入到新的散列表内,因此开销较大。但平均而言,即使考虑到调整长度所需的时间,散列表操做所需的时间也为O(1)。

填装因子越低,发生冲突的可能性越小,散列表的性能越高。一个不错的经验规则是:一旦填装因子大于0.7,就调整散列表的长度。

  • 良好的散列函数。

良好的散列函数让数组中的值呈均匀分布。糟糕的散列函数让值扎堆,致使大量的冲突。

请继续关注个人公众号文章
图片描述

相关文章
相关标签/搜索