0204:数据结构-散列表

发表信息: by Creative Commons Licence

本文依照学习难度,分成入门、进阶两个部分。

入门部分以直观、易于理解为主;进阶部分重点在于概念、推导等。

目录

入门

散列表是什么

散列表,又被成为散列映射、映射、字典和关联数组。由键和值组成,直观来看是:

查询某一种水果时,可以立即获得对应的价格。

散列表和数组、链表有什么关系呢?

一句话来说,散列函数和数组共同创建了散列表

数组和链表都是被直接映射到内存的

散列表则使用散列函数来确定元素的存储位置

也就是散列表是,数组中的元素多经过一道函数计算后(散列函数),再映射到内存。

是数组的拓展

到这一步为止,知道了有散列函数这么个神奇的事物存在,后面再继续相似了解。

前面的例子,希望实现的是:当查找苹果时,会告诉我们苹果的价格是3.

换句说,我们有一个放价格数据的数组:

希望能够通过散列函数将水果准确映射到价格数组上

如上图,通过散列函数即可得到索引,而得到了索引就可以充分利用数组随机查找的优点。

关键在于散列函数

散列函数想要实现上述获取索引的功能,至少要满足

一致性: “苹果”永远映射为1,每次映射都得到相同的索引值

差异性:“苹果”和“梨”应该映射的索引不相同。保证不同的输入映射到不同的数字。

为保证效率,散列函数还应知道数组有多大,像上面长度为3的数组,返回的索引应在0~2之间,不会返回无效索引,如100

散列表的应用

1、DNS解析(DNS resolution)

非常常见的将网址映射到IP地址。

2、防止重复 比如投票,防止重复投票。每个人投票,就将这个人的姓名映射到一个固定的数值上,如果这个人曾经投过票就很容易查询出来。

3、缓存

缓存就是网站将数据记住,而不是每次收到用户打开的命令都重新计算。

这时候就是将页面URL映射到页面数据上,如下图:

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

在实际使用散列表时,需要考虑性能等因素,情况会更复杂。

因此,如何实现散列函数,接下来做进一步的探讨。

实现散列函数的一些算法

  1. SHA算法(secure hash algorithm,安全散列算法)

这是一个系列,包括SHA-0,SHA-1,SHA-2,SHA-3

实现结果例如 “hello” ==> "2cf24db"

可通过SHA判断两个文件是否相同

计算密码的散列值

目前最安全的密码散列函数是 brypt 局部不敏感 Simbash 局部敏感(检查两项内容相似程度时很有用)

  1. Diffie-Hellman 密钥交换

对消息加密

使用两个密钥:公钥和私钥。

有人向你发消息时,使用公钥对其进行加密。加密的消息只有自己的私钥才能打开,公钥可以用各种途径传播。

优良的散列函数

良好的散列函数让数组中的值呈均匀分布

散列函数的结果必须是均匀分布,这很重要

它们的映射范围必须尽可能的大

最糟糕的散列函数莫过于将所有输入都映射到散列表的同一位置

填装因子越低,发生冲突的可能性越小,散列表的性能越高

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

经验规则:一旦填装因子大于0.7,就调整散列表的长度

当然调整长度的开销也挺大。

进阶

[1] 算法图解 Aditya Bhargava (作者) 袁国忠 (译者)