数据结构与算法——17. 散列(哈希)与完美散列函数
文章目录
- 一、散列(Hashing)的概念
- 举例说明
- 二、完美散列函数
- 1. 数据的一致性校验
- 2. 完美散列函数用于数据的一致性校验
一、散列(Hashing)的概念
在查找算法中,如果数据项之间是按照大小排好序的话,就可以利用二分查找来降低算法复杂度。现在我们沿着这个思路,在存放数据时花点心思:构造一个新的数据结构,能使得查找算法的复杂度降到 O ( 1 ) O(1) O(1)。
如果我们事先能知道要找的数据项应该出现在数据集中的什么位置,就可以直接到那个位置看看数据项是否存在即可。于是,现在的问题就成了:如何由数据项的值来确定其存放位置?
散列表(hash table,又称哈希表)是一种数据集,其存储数据项的方式,有利于将来快速的查找定位。
散列表中的每一个存储位置,称为槽(slot),可以用来保存数据项,每个槽有一个唯一的名称。
实现从数据项到存储槽名称的转换的,称为散列函数(hash function)。
而散列就是指散列函数所进行的那一过程,即将任意数据项转换为固定长度输出的过程。
举例说明
例如:一个包含11个槽的散列表,槽的名称分别为0~10。在插入数据项之前, 每个槽的值都是None,表示空槽。
散列函数接受数据项作为参数,返回整数值0~10,表示数据项存储的槽号(名称)。
“求余数”是一种常用的散列方法,将数据项除以散列表的大小,得到的余数作
为槽号。因为散列函数返回的槽号必须在散列表大小范围之内,所以一般会对散列表大小求余。(实际上“求余数”方法会以不同形式出现在所有散列函数里)
我们这里也使用求余运算作为散列函数: h ( i t e m ) = i t e m % 11 h(item) = item\% 11 h(item)=item%11
数据项(item) | 散列值(hash value) |
---|---|
54 | 10 |
26 | 4 |
93 | 5 |
17 | 6 |
77 | 0 |
31 | 9 |
按照散列函数 h ( i t e m ) h(item) h(item),为每个数据项计算出存放的位置之后,就可以将数据项存入相应的槽中。槽被数据项占据的比例称为散列表的“负载因子”,这里负载因子为6/11。
数据项都保存到散列表后,查找就无比简单:我们只需要使用同一个散列函数,对查找项进行计算,测试下返回的槽号所对应的槽中是否有数据项即可。实现了 O ( 1 ) O(1) O(1)时间复杂度的查找算法。
此时,我们发现了一个问题:上面的数据相当凑巧,各自占据了不同槽,假如还要保存44, h ( 44 ) = 0 h(44)=0 h(44)=0,它跟77被分配到同一个0号槽中,这种情况称为“冲突collision”,我们后面会讨论到这个问题的解决方案。
二、完美散列函数
给定一组数据项,如果一个散列函数能把每个数据项映射到不同的槽中,那么这个散列函数就可以称为“完美散列函数”。
对于固定的一组数据,总是能想办法设计出完美散列函数。但如果数据项经常性的变动,就很难有一个系统性的方法来设计对应的完美散列函数。当然,冲突也不是致命性的错误,我们会有办法处理的。
获得完美散列函数的一种方法是扩大散列表的容量,大到所有可能出现的数据项都能够占据不同的槽。但这种方法对于可能数据项范围过大的情况并不实用,假如我们要保存手机号(11位数字),完美散列函数得要求散列表具有百亿个槽!会浪费太多存储空间。
综上,好的散列函数需要具备特性:
- 冲突最少(近似完美);
- 计算难度低(额外开销小);
- 充分分散数据项(尽量均匀分布在各个槽中,以节约空间)。
1. 数据的一致性校验
由于完美散列函数能够对任何不同的数据生成不同的散列值,如果把散列值当作数据的“指纹”或者“摘要”,这种特性被广泛应用在数据的一致性校验上。
作为一致性校验的数据“指纹”函数需要具备如下的特性:
- 压缩性:任意长度的数据,得到的“指纹”长度是固定的;
- 易计算性:从原数据计算“指纹”很容易;(从指纹计算原数据是不可能的);
- 抗修改性:对原数据的微小变动,都会引起“指纹”的大改变;
- 抗冲突性:已知原数据和“指纹”,要找到相同指纹的数据(伪造)是非常困难的。
最著名的近似完美散列函数是MD5和SHA系列函数:
- MD5(Message Digest)将任何长度的数据变换为固定长为128位(16字节)的“摘要”。128位二进制已经是一个极为巨大的数字空间:据说是地球沙粒的数量。
- SHA(Secure Hash Algorithm)是另一组散列函数:
- SHA-0/SHA-1输出散列值160位(20字节),160位二进制相当于10的48次方,地球上水分子数量估计是47次方。
- SHA-256/SHA-224分别输出256位、224位,256位二进制相当于10的77方,已知宇宙所有基本粒子大约是72~87次方。
- SHA-512/SHA-384分别输出512位和384位。
虽然近年发现MD5/SHA-0/SHA-1三种散列函数,能够以极特殊的情况来构造个别碰撞(散列冲突),但在实用中从未有实际的威胁。
Python自带MD5和SHA系列的散列函数库:hashlib,包括了md5 / sha1 / sha224 / sha256 / sha384 / sha512等6种散列函数。简单用法可以参考我的文章:传送门。
2. 完美散列函数用于数据的一致性校验
为每个文件计算其散列值,仅对比其散列值即可得知是否文件内容相同。用于网络文件下载完整性校验:
- 文件下载:文件提供网站通常会提供散列值,可以帮助用户校验下载的文件是否有损坏,或者被篡改过。
- 网盘秒传:我们在向网盘上传文件时(尤其是电影),由网盘客户端在本地计算好散列值,上传给网盘服务器,网盘服务器就会寻找是否已有与该散列值相同散列值的文件。如果有,就说明该文件已经被人上传到了,无需重复上传,只需将已有文件链接到我们的账户下,以此实现秒传。
- 密码保存:加密形式保存密码,仅保存密码的散列值,用户输入密码后,计算散列值并比对;无需保存密码的明文即可判断用户是否输入了正确的密码。
- ……
数据结构与算法——17. 散列(哈希)与完美散列函数相关推荐
- 数据结构与算法 ~ 查找 ~ 散列查找(哈希~线性探查法和二次探查法)
数据结构与算法 ~ 查找 ~ 散列查找(哈希~线性探查法和二次探查法) /*search-hash*/ #include<math.h> #include<stdio.h> # ...
- Java 国密算法 SM3 散列哈希使用
Java 国密算法 SM3 散列哈希使用 文章目录 Java 国密算法 SM3 散列哈希使用 引入依赖 计算散列哈希 引入依赖 <dependency><groupId>org ...
- 数据结构之查找算法:散列查找
查找算法:散列查找 思维导图: 散列函数和散列表: 构造散列函数的要求: 构造散列函数的方法: 直接定址法: 除留取余法: 数字分析法: 平方取中法: 折叠法: 解决冲突的方法: 开放定址法: 线性探 ...
- 10_JavaScript数据结构与算法(十)哈希表
JavaScript 数据结构与算法(十)哈希表 认识哈希表 哈希表是一种非常重要的数据结构,几乎所有的编程语言都直接或者间接应用这种数据结构. 哈希表通常是基于数组实现的,但是相对于数组,它存在更多 ...
- 数据结构与算法笔记(十五)—— 散列(哈希表)
一.前沿 1.1.直接寻址表 当关键字的全域U比较小时,直接寻址是一种简单而有效的技术.假设某应用要用到一个动态集合,其中每个元素都有一个取自全域U={0,1,-,m-1)的关键字,此处m是一个不很大 ...
- 聊聊传说中的散列哈希Hash算法,以及Java中的HashTable,HashMap,HashSet,ConcurrentHashMap......
建议本文结合java源码来阅读,看了之后就什么都懂了,还有参考文献. 散列(Hash) 是一种按关键字编址的存储和检索方法 散列表(HashTable)根据元素的关键字确定元素的位置 散列函数(Has ...
- Java数据结构和算法:HashMap,哈希表,哈希函数
1. HashMap概述 HashMap是基于哈希表的Map接口的非同步实现(Hashtable跟HashMap很像,唯一的区别是Hashtalbe中的方法是线程安全的,也就是同步的).此实现提供所有 ...
- 【数据结构与算法】散列表
一.散列表的由来? 1.散列表来源于数组,它借助散列函数对数组这种数据结构进行扩展,利用的是数组支持按照下标随机访问元素的特性. 2.需要存储在散列表中的数据我们称为键,将键转化为数组下标的方法称为散 ...
- 【Java数据结构与算法】第十章 哈希表和二叉树
第十章 哈希表和二叉树 文章目录 第十章 哈希表和二叉树 一.哈希表 1.介绍 2.代码实现 二.二叉树 1.介绍 2.遍历二叉树 3.查找二叉树 4.二叉树删除节点 5.二叉树综合实例 一.哈希表 ...
最新文章
- 《HTML5与CSS3实战指南》——2.5 构建The HTML5 Herald
- SAP QM 事务代码QAC2的BUG?
- mysql进阶(十五) mysql批量删除大量数据
- 音视频技术开发周刊 84期
- 利用微信登录掘金网站的HTTP请求分析
- Express 极速掌握
- python就业方向-看完Python这五大就业方向的薪资待遇,你选择哪个?
- Opencv单目标定flag的设定
- android alertdialog 自定义时间,Android自定义dialog可选择展示年月日时间选择栏
- VC动态库可以嵌套调用
- 微信/qq/防撤回插件
- 在 Java 应用程序中访问 USB 设备
- 百度url提交入口 百度网站收录提交入口网址
- 【动态规划】数字三角形c语言
- vue实现导出表格数据
- 活动并发测试-1000个不同用户同时并发请求报名笔记
- 计算机控制运行内存,运行内存
- 计算机专业高级工程师考哪些专业,高级工程师职称考试项目有哪些
- CSDN是怎么样的一个网站
- 解读新零售:什么叫做人货场的重构
热门文章
- 【微信小程序】小程序实现文件的上传及预览,以PDF文件为例。
- 什么是闭包?闭包的优缺点? 1
- 使用Nightwatch进行端到端测试
- Windows上安装Linux
- Pytorch-模型参数:named_parameters()、parameters()、state_dict()区别
- Fiddler4 手机抓包
- 【观察】揭秘:中信银行信用卡新核心系统自研之路
- 在CMD上运行javac前应该这样做
- Timer延时任务和ScheduledThreadPool执行延时任务
- scanf()函数的用法