数据结构与算法Python版之北大慕课笔记(三)

一、散列:Hashing

1. 引入散列

  • 通过构造一个新的数据结构,能使得查找算法的复杂度降到O(1),这种概念称为“散列Hashing”。
  • 能够使得查找的次数降低到常数级别,我们对数据项所处的位置就必须有更多的先验知识。
  • 如果我们事先能知道要找的数据项应该出现在数据集中的什么位置,就可以直接到那个位置看看数据项是否存在即可。

2. 散列基础知识

  • 散列表(hash table,又称哈希表)是一种数据集,其中数据项的存储方式尤其有利于将来快速的查找定位。
  • 散列表中的每一个存储位置,称为槽(slot),可以用来保存数据项,每个槽有一个唯一的名称。

例如:一个包含11个槽的散列表,槽的名称分别为0~10,在插入数据项之前,每个槽的值都是None,表示空槽。

在这里插入图片描述
实现从数据项到存储槽名称的转换的,称为散列函数(hash function),下面示例中,散列函数接受数据项作为参数,返回整数值0~10,表示数据项存储的槽号(名称)。

  • 为了将数据项保存到散列表中,我们设计第一个散列函数。数据项:54,26,93,17,77,31.
  • 有一种常用的散列方法是“求余数”,将数据项除以散列表的大小,得到的余数作为槽号。实际上“求余数”方法会以不同形式出现在所有散列函数里,因为散列函数返回的槽号必须在散列表大小范围之内,所以一般会对散列表大小求余。
  • 本例中我们的散列函数是最简单的求余: h(item) = item % 11
  • 按照散列函数h(item),为每个数据项计算出存放的位置之后,就可以将数据项存入相应的槽中。

在这里插入图片描述

  • 例子中的6个数据项插入后,占据了散列表11个槽中的6个。槽被数据项占据的比例称为散列表的“负载因子”,这里负载因子为 6/11。
  • 数据项都保存到散列表后,查找就无比简单。要查找某个数据项是否存在与表中,我们只需要使用同一个散列函数,对查找项进行计算,测试下返回的槽号所对应的槽中是否有数据项即可。实现了O(1)时间复杂度的查找算法。
  • 假如还要保存44,h(44)=0,他会跟77被分配到同一个槽中,这种情况称为“冲突collosion”。

3. 完美散列函数

  • 给定一个数据项,如果一个散列函数能把每个数据项映射到不同的槽中,那么这个散列函数就可以称为“完美散列函数”。对于固定的一组数据,总是能想办法设计出完美的散列函数。

  • 但如果数据项经常性的变动,很难有一个系统性的方法来设计对应的完美散列函数。当然,冲突也不是致命性的错误,有办法解决。

  • 获得完美散列函数的一种方法是扩大散列表的容量,大到所有可能出现的数据项都能够占据不同的槽。但这种方法对于可能数据项范围过大的情况并不适用。假如我们要保存手机号(11位数字),完美散列函数得要求散列表具有百亿个槽,会浪费太多存储空间。

  • 退而求其次,好的散列函数需要具备特性:冲突最少(近似完美)、计算难度低(额外开销小)、充分分散数据项(节约空间)。

  • 由于完美散列函数能够对任何不同的数据生成不同的散列值,如果把散列值当作数据的“指纹”或者“摘要”,这种特性被广泛应用在数据的一致性校验上。

  • 作为一致性校验的数据“指纹”函数需要具备如下的特性:

       1. 压缩性:任意长度的数据,得到的“指纹”长度是固定的。
       2. 易计算性:从原数据计算“指纹”很容易,从指纹计算原数据是不可能的。
       3. 抗修改性:对原数据的微小变动,都会引起“指纹”的大改变。
       4. 抗冲突性:已知原数据和“指纹”,要找到相同指纹的数据(伪造)是非常困难的。
    
3.1 散列函数MD5/SHA
  • 最著名的近似完美散列函数是MD5和SHA系列函数。
  • MD5(Message Digest)将任何长度的数据变换为固定长为128位(16字节)的摘要。
  • SHA(Secure Hash Algorithm)是另一组散列函数。
    1. SHA-0 / SHA-1 输出散列值160位(20字节)
    2. SHA-256 / SHA-224 分别输出256位、224位
    3. SHA-512 / SHA-384分别输出512位和384位
3.2 Python的散列函数库hashlib

Python自带MD5和SHA系列的散列函数库:hashlib,包括了md5 / sha1 / sha224 / sha256 / sha384 / sha512等6种散列函数。

import hashlib
hashlib.md5("hello world!").hexdigest()
hashlib.sha1("hello world!").hexdigest()

除了对单个字符串进行散列计算之外,还可以用update方法来对任意长的数据分部分来计算,这样不管多大的数据都不会有内存不足的问题。

import hashlib

m = hashlib.md5()
m.update("hello world!")
m.update("this is part #2")
m.update("this is part #3")
m.hexdigest()

二、散列函数最酷应用:区块链

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值