图解算法学习笔记(五):散列表

本章内容:

       学习散列表,最有用的数据结构之一。

       学习散列表的内部机制:实现、冲突和散列函数。

1)示例1:

假设你在一家杂货店上班,有顾客来买东西时,你得在本子中查找价格。第一章介绍的简单查找,需要O(n)时间,如果你使用的是二分查找,时间为O(log n)

二分查找的速度已经很快了,但作为收银员,在本子中查找价格是件很痛苦的事,即使本子的内容是有序的。在查找价格时,你都能感觉到顾客的怒气。但如果我们有一位雇员(Maggie),她能记住所有商品的价格,问她就能马上知道答案。这位雇员报出任何商品的价格的时间为O(1)

2)散列函数

散列函数是这样的函数,无论你给它什么样的数据,它都还给你一个数字。

你可能认为散列函数输出的数字没什么规律,但其实散列函数必须满足一些要求。

  • 它必须是一致的,输出与输入保持一致;
  • 它应将不同的输入映射到不同的数字;

现在,我们可以打造我们的"Maggie"了。

首先创建一个空数组:

现在,我们将苹果的价格加入到数组中,输入为apple时,散列函数的输入为3,因此我们将苹果的价格存储到数组的索引3处。

不断地重复这个过程,最终整个数组将填满价格。

现在假设需要知道鳄梨(avocado)的价格,你无需在数组中查找,只需将avocado作为输入交给散列函数。,它会告诉你价格存储在索引4处。

散列函数之所以能准确地指出价格的存储位置,具体原因如下:

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

现在,我们可以结合散列函数和数组创建一个被称为散列表(hash table)的数据结构,在学习的复杂数据结构中,散列表可能是最有用的。Python提供的散列表实现为字典

3)应用案例

散列表用途广泛,具体有:

  • 手机里的电话簿,DNS解析;
  • 防止重复;
  • 将散列表用作缓存,缓存是一种常用的加速方式,所有的大型网站都使用缓存,缓存的数据存储在散列表中;

4)冲突

要明白散列表的性能,我们先搞清什么是冲突,现在我们有一个数组,它包含26个位置。

使用的散列函数非常简单,它按字母表顺序分配数组的位置。

现在,我们分别将苹果,香蕉、鳄梨的价格存储到散列表中,会出现下面这种情况:

这时出现了冲突(collision):给两个键分配的位置相同

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

通过上面的介绍,我们了解到散列函数很重要,理想的是散列函数将键均匀地映射到散列表的不同位置

5)性能

散列表查找、插入、删除的运行时间如图所示:

在使用散列表是,避免最糟情况至关重要,为此,需要避免冲突,需要有:较低的填装因子,良好的散列函数

填装因子度量的是散列表有多少位置是空的。一个经验是:一旦填装因子大于0.7,就调整散列表的长度。而什么是良好的散列函数,这不需要我们操心——天塌下来有高个子顶着。

6)小结

冲突很糟糕,你应使用可以最大限度减少冲突的散列函数。

散列表的查找、插入和删除速度都非常快。

散列表适合用于模拟映射关系。
一旦填装因子超过0.7,就该调整散列表的长度。

散列表可用于缓存数据(例如,在Web服务器上)。

散列表非常适合用于防止重复。

猜你喜欢

转载自blog.csdn.net/cg129054036/article/details/83511824
今日推荐