关于散列的一些学习笔记

散列表

散列函数:传入一个数据,返回一个数字。

即:将输入映射到数字。

散列函数必须满足一些要求:
1、一致性:同样的输入映射的是相同的数字。
2、差异性:将不同的输入映射到不同的数字。

应用:散列表(hash table)。

别称:散列映射、映射、字典和关联数组。

任一优秀的语言都提供了散列表实现。Python提供的散列表实现为字典

散列表的优势:

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

现实生活中的案列:

散列表被用于大海捞针式的查找:
DNS解析使用的就是散列表提供的功能
你在访问类似http://adit.io这样的网站时,计算机必须将adit.io转换为IP地址。
这就是将网址映射到IP地址。

检查重复

使用散列表来检查是否重复,速度非常快,而不必使用简单查找去搜索整个列表。

用作缓存

目的:让web服务器少做工作,提高网站的访问速度。
原理:记住每次访问网站的数据,下一次再次访问时直接使用上一次访问得到的数据,而不用再去请求服务器

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

冲突

关于前面散列函数的叙述“散列函数总是将不同的键映射到数组的不同位置”实际上是不合适的。

实际上几乎不可能编写出这样的散列函数。

这种情况下冲突就产生了:我们会给两个键分配相同的位置,而先分配的键会被后分配的键覆盖。

例如:假设我有一个数组,它包含26个位置。而我使用的散列函数非常简单,它按字母表顺序分配数组的位置。

现在我要将苹果Apple的价格存储到散列表中,它将被分配到散列表的第一个位置上。
接下来我要将香蕉Banana的价格存储到散列表中,它将被分配到散列表的第二个位置上。
但是现在我要将鳄梨Avocados的价格存储到散列表中,它又将被分配到散列表的第一个位置。

这个时候你会说不好了,第一个位置我已经存储上苹果Apple的价格了,这个时候如果给鳄梨分配位置,那么我以后查询苹果的价格实际上得到的是鳄梨的价格。

如何解决冲突

1、最简单的办法如下:
如果两个键映射到了同一个位置,就在这个位置存储一个链表。
如下图:
88888

经验教训

1、散列函数很重要。理想的散列函数是将键均匀的映射到散列表的不同位置。
2、如果散列表存储的链表很长,散列表的速度将急剧下降。

性能

**在平均情况下,散列表执行各种操作的时间都为O(1),即常量时间。**也就是说不管散列表有多大,所需的时间都相同。

在平均情况下,散列表的查找(获取给定索引处的值)速度与数组一样快,而插入和删除的速度与链表一样快,因此它兼具两者的优点!
如图:
在这里插入图片描述

但是在最糟情况下,散列表的各种操作的速度都很慢。因此,在使用散列表时,避开最糟情况至关重要。为此,需要避免冲突。而要避免冲突,需要具备以下条件
1、较低的填装因子。
2、良好的散列函数。

填装因子

填装因子的计算:

       散列表包含的元素数/位置总数

填装因子用来度量散列表中有多少位置是空的

最佳情况下每个元素都有自己的位置,然而如果没有足够的位置即填装因子大于1则意味着元素数量超过了数组的位置数。一旦填装因子开始增大,你就需要在散列表中添加位置,这被称为调整长度

为此我们首先需要创建一个更长的新数组:通常将数组增长一倍。

接下来,我们使用散列函数将所有的元素都插入到这个新的散列表中。

**结论:**填装因子越低,发生冲突的可能性越小,散列表的性能越高。
**经验规则:**一旦填装因子大于0.7,就调整散列表的长度。
通常情况下,即便考虑到调整长度需要的时间,散列表操作所需的时间也为O(1)。

良好的散列函数

良好的散列函数能让数组中的值呈均匀分布;反之,糟糕的散列函数会让值扎堆,导致大量的冲突出现。

例如:SHA函数

猜你喜欢

转载自blog.csdn.net/june_young_fan/article/details/83212142
今日推荐