哈希

散列(哈希)是电脑科学中一种对资料的处理方法,通过某种特定的函数/算法(称为散列函数/算法)将要检索的项与用来检索的索引(称为散列,或者散列值)关联起来,生成一种便于搜索的数据结构(称为散列表)。

哈希表是什么

哈希表(散列表)是根据键(Key)直接访问内存存储位置的数据结构。根据键(Key)值将数据映射到内存中一个位置的函数称为哈希函数,根据哈希函数建立的记录数据的表称为哈希表。

哈希表的特点

  • 若关键字为 ,则其值存放在的存储位置上。由此,不需比较便可直接取得所查记录。称这个对应关系  为散列函数,按这个思想建立的表为散列表。

  • 对不同的关键字可能得到同一散列地址,即 ,而 ,这种现象称为冲突。

  • 若对于关键字集合中的任一个关键字,经散列函数映象到地址集合中任何一个地址的概率是相等的,则称此类散列函数为均匀散列函数(Uniform Hash function),这就是使关键字经过散列函数得到一个“随机的地址”,从而减少冲突。

处理冲突

开放定址法

开放定址法就是产生冲突之后去寻找下一个空闲的空间。函数定义为:

其中,hash(key) 是哈希函数, 是增量序列, 为已冲突的次数。

  • 线性探测法:,或者其他线性函数。相当于逐个探测存放地址的表,直到查找到一个空单元,然后放置在该单元。

  • 平方探测法:

链表法

这是另外一种类型解决冲突的办法,散列到同一位置的元素,不是继续往下探测,而是在这个位置是一个链表,这些元素则都放到这一个链表上。

再散列

如果一次不够,就再来一次,直到冲突不再发生。

建立公共溢出区

将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表(注意:在这个方法里面是把元素分开两个表来存储)。

哈希表的应用

  • 查字典

  • 网络防火墙中,根据源IP,目的IP,源端口,目的端口,协议号构成的五元组来标识一条网络数据流的,并且根据五元组来建立会话表项(session entry)。为了查找便捷,一般都使用Hash表来实现这个会话表,以提高转发的效率。

  • Linux 内核大量采用哈希表

下面用开放地址法(线性探测)实现了一个简单的哈希表。仔细阅读后试试哈希表的各种操作,感受以下哈希表飞一般的速度吧!

代码段 1

class Hash:# 表的长度定位11def __init__(self):self.hash_table=[[None,None]for i in range(11)]# 散列函数def hash(self,k,i):h_value=(k+i)%11if self.hash_table[h_value][0]==k:return h_valueif self.hash_table[h_value][0]!=None:i+=1h_value=self.hash(k,i)return h_valuedef put(self,k,v):hash_v=self.hash(k,0)self.hash_table[hash_v][0]=kself.hash_table[hash_v][1]=vdef get(self,k):hash_v=self.hash(k,0)return self.hash_table[hash_v][1]hash = Hash()
hash.put(1 ,'wang')
print(hash.get(1))

上述代码实现了一个简单的哈希表,但表的长度只有11,填入表中元素越来越多后,产生冲突的可能性会越来越大。

由此引出另一个概念,叫做载荷因子(load factor)。载荷因子的定义为:

所以当达到一定程度后,表的长度要变的,载荷因子被设计为0.75;超过0.8,cpu 的cache missing 会急剧上升。即需要新定义一个 resize 函数扩大表的长度,一般选择扩到已插入元素数量的两倍。

在上述代码中重新升级我们的 Hash 吧!示例代码代码段2中。

代码段 2

class Map:def __init__(self):self.capacity=11self.hash_table=[[None,None]for i in range(self.capacity)]self.num=0self.load_factor=0.75def hash(self,k,i):h_value=(k+i)%self.capacityif self.hash_table[h_value][0]==k:return h_valueif self.hash_table[h_value][0]!=None:i+=1h_value=self.hash(k,i)return h_valuedef resize(self):#扩容到原有元素数量的两倍self.capacity=self.num*2temp=self.hash_table[:]self.hash_table=[[None,None]for i in range(self.capacity)] for i in temp:#把原来已有的元素存入if(i[0]!=None):hash_v=self.hash(i[0],0)self.hash_table[hash_v][0]=i[0]self.hash_table[hash_v][1]=i[1]def put(self,k,v):hash_v=self.hash(k,0)self.hash_table[hash_v][0]=kself.hash_table[hash_v][1]=v#暂不考虑key重复的情况,具体自己可以优化self.num+=1# 如果比例大于载荷因子if(self.num/len(self.hash_table)>self.load_factor):self.resize()def get(self,k):hash_v=self.hash(k,0)return self.hash_table[hash_v][1]

经典实践

Python 中的字典就是用哈希表来实现的,它的特点如下:

  • 字典的每个键值 key=>value 对用冒号 : 分割,每个键值对之间用逗号 , 分割,整个字典包括在花括号 {} 中 ,格式:dict = {key1 : value1, key2 : value2 }

  • 通过中括号访问,添加,更新元素

dictionary = {'name': 'wang', 'age': 17, 'class': 'first'}
dictionary['age'] = 18
dictionary['country'] = 'china'
print(dictionary['age'])
print(dictionary['country'])

根据 Python 中的字典特性,自己手动实现一个 Python 字典吧

class MyDictionary(object):# 字典类的初始化def __init__(self):self.table_size = 13 # 哈希表的大小self.key_list = [None]*self.table_size #用以存储key的列表self.value_list = [None]*self.table_size #用以存储value的列表# 散列函数,返回散列值# key为需要计算的keydef hashfuction(self, key):count_char = 0key_string = str(key)for key_char in key_string: # 计算key所有字符的ASCII值的和count_char += ord(key_char) # ord()函数用于求ASCII值length = len(str(count_char))if length > 3 : # 当和的位数大于3时,使用平方取中法,保留中间3位mid_int = 100*int((str(count_char)[length//2-1])) \+ 10*int((str(count_char)[length//2])) \+ 1*int((str(count_char)[length//2+1]))else: # 当和的位数小于等于3时,全部保留mid_int = count_charreturn mid_int%self.table_size # 取余数作为散列值返回# 重新散列函数,返回新的散列值# hash_value为旧的散列值def rehash(self, hash_value):return (hash_value+3)%self.table_size #向前间隔为3的线性探测# 存放键值对def __setitem__(self, key, value):hash_value = self.hashfuction(key) #计算哈希值if None == self.key_list[hash_value]: #哈希值处为空位,则可以放置键值对passelif key == self.key_list[hash_value]: #哈希值处不为空,旧键值对与新键值对的key值相同,则作为更新,可以放置键值对passelse: #哈希值处不为空,key值也不同,即发生了“冲突”,则利用重新散列函数继续探测,直到找到空位hash_value = self.rehash(hash_value) # 重新散列while (None != self.key_list[hash_value]) and (key != self.key_list[hash_value]): #依然不能插入键值对,重新散列hash_value = self.rehash(hash_value) # 重新散列#放置键值对      self.key_list[hash_value] = keyself.value_list[hash_value] = value# 根据key取得valuedef __getitem__(self, key):hash_value = self.hashfuction(key) #计算哈希值first_hash = hash_value #记录最初的哈希值,作为重新散列探测的停止条件if None == self.key_list[hash_value]: #哈希值处为空位,则不存在该键值对return Noneelif key == self.key_list[hash_value]: #哈希值处不为空,key值与寻找中的key值相同,则返回相应的value值return self.value_list[hash_value]else: #哈希值处不为空,key值也不同,即发生了“冲突”,则利用重新散列函数继续探测,直到找到空位或相同的key值hash_value = self.rehash(hash_value) # 重新散列while (None != self.key_list[hash_value]) and (key != self.key_list[hash_value]): #依然没有找到,重新散列hash_value = self.rehash(hash_value) # 重新散列if hash_value == first_hash: #哈希值探测重回起点,判断为无法找到了return None#结束了while循环,意味着找到了空位或相同的key值if None == self.key_list[hash_value]: #哈希值处为空位,则不存在该键值对return Noneelse: #哈希值处不为空,key值与寻找中的key值相同,则返回相应的value值return self.value_list[hash_value]# 删除键值对def __delitem__(self, key):hash_value = self.hashfuction(key) #计算哈希值first_hash = hash_value #记录最初的哈希值,作为重新散列探测的停止条件if None == self.key_list[hash_value]: #哈希值处为空位,则不存在该键值对,无需删除returnelif key == self.key_list[hash_value]: #哈希值处不为空,key值与寻找中的key值相同,则删除self.key_list[hash_value] = Noneself.value_list[hash_value] = Nonereturnelse: #哈希值处不为空,key值也不同,即发生了“冲突”,则利用重新散列函数继续探测,直到找到空位或相同的key值hash_value = self.rehash(hash_value) # 重新散列while (None != self.key_list[hash_value]) and (key != self.key_list[hash_value]): #依然没有找到,重新散列hash_value = self.rehash(hash_value) # 重新散列if hash_value == first_hash: #哈希值探测重回起点,判断为无法找到了return#结束了while循环,意味着找到了空位或相同的key值if None == self.key_list[hash_value]: #哈希值处为空位,则不存在该键值对returnelse: #哈希值处不为空,key值与寻找中的key值相同,则删除self.key_list[hash_value] = Noneself.value_list[hash_value] = Nonereturn# 返回字典的长度def __len__(self):count = 0for key in self.key_list:if key != None:count += 1return countdef main():H = MyDictionary()H["kcat"]="cat"H["kdog"]="dog"H["klion"]="lion"H["ktiger"]="tiger"H["kbird"]="bird"H["kcow"]="cow"H["kgoat"]="goat"H["pig"]="pig"H["chicken"]="chicken"print("字典的长度为%d"%len(H))print("键 %s 的值为为 %s"%("kcow",H["kcow"]))print("字典的长度为%d"%len(H))print("键 %s 的值为为 %s"%("kmonkey",H["kmonkey"]))print("字典的长度为%d"%len(H))del H["klion"]print("字典的长度为%d"%len(H))print(H.key_list)print(H.value_list)if __name__ == "__main__":main()

-END-

想学习更对技术技能加下方小姐姐微信,领取全套源码笔记教程

Python数据结构:哈希表相关推荐

  1. Python中常用的数据结构---哈希表(字典)

    Python中常用的数据结构-哈希表(字典) 常用的数据结构有数组.链表(一对一).栈和队列.哈希表.树(一对多).图(多对多)等结构. 在本目录下我们将讲解,通过python语言实现常用的数据结构. ...

  2. python线性表和队列_[笔记]python数据结构之线性表:linkedlist链表,stack栈,queue队列...

    python数据结构之线性表 python内置了很多高级数据结构,list,dict,tuple,string,set等,在使用的时候十分舒心.但是,如果从一个初学者的角度利用python学习数据结构 ...

  3. java hashtable 数据结构_数据结构--哈希表(Java)

    数据结构--哈希表(Java) 介绍 哈希表 底层是 数组加链表 或者是 数组加二叉树 ,一个数组里面有多个链表,通过散列函数来提高效率 代码 package cn.guizimo.hashtab; ...

  4. 七十五、Python | Leetcode哈希表系列

    @Author:Runsen @Date:2020/7/3 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  5. python leetcode_七十五、Python | Leetcode哈希表系列

    @Author:Runsen @Date:2020/7/3 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  6. python实现哈希表

    # python 实现哈希表class HashTable:"""哈希函数的构造解决冲突"""def __init__(self, sour ...

  7. Python 数据结构 之 线性表 的链式存储结构

    用Python 来实现 C语言中 线性表的链式存储结构. 文章转载请注明:  Python 数据结构 之 线性表 的链式存储结构 代码地址 https://github.com/WenkeZhou/P ...

  8. 数据结构——哈希表的详解与实现

    数据结构--哈希表(HashTable) 1.前言 ​ 当我们频繁的查找数据中的某个元素时,我们通常会选择数组来存放数据,因为数组的的内存是连续的,可以直接通过下标访问数据,但是它添加和删除数据比较麻 ...

  9. 数据结构哈希表的实现与设计

    数据结构哈希表查找姓名的课程设计 有没有大神能帮忙写一下这道题,课设的题目.用C++语言 问题描述:针对某公司中花名设计哈希表,并完成相应的建表和查表程序,基本要求: (1)假设花名为汉字拼音形式.名 ...

  10. 算法笔记(三)特殊数据结构——哈希表、有序表、并查集、KMP、Manacher、单调栈、位图、大数据类题

    layout: post title: 算法笔记(三)特殊数据结构--哈希表.有序表.并查集.KMP.Manacher.单调栈.位图.大数据类题 description: 算法笔记(三)特殊数据结构- ...

最新文章

  1. (已解决)module ‘tensorflow‘ has no attribute ‘app‘
  2. Linux下的一个图形管理工具webmin
  3. 文字超长自动加省略号的css写法
  4. python线程代码_python--(十步代码学会线程)
  5. 函数递归方法反转字符串
  6. 单精度浮点数与十六进制转换
  7. iOS-数据持久化-属性列表
  8. Entity Framework关联实体的三种加载方法
  9. [HTML/HTML5]5 使用链接
  10. 前端----表格的具体使用(jquery)
  11. 颜宁谈为何选择深圳:一拍即合!我麻溜地向普林斯顿递了辞职申请
  12. python在线题库推荐_Python题库.docx
  13. 真正会沟通的项目经理,不会告诉你的4件事
  14. “头号电脑黑客” 凯文.米特尼克 与 中国台湾的”电脑鬼才“陈盈豪
  15. 彻底解决Ubuntu18.04搜狗拼音输入法问题---支持Pycharm、WPS
  16. Flume之生产正确的使用方式二(Multiple Agent+ Multiple Channel+Custom)
  17. 计算机点击管理无效,win10开始菜单没反应,二种解决办法!
  18. 《JAVA与模式》— 调停者模式
  19. 第73节:Java中的HTTPServletReauest和HTTPServletResponse
  20. 【已解决】笔记本HDMI接口外接显示器调不了分辨率,无信号

热门文章

  1. 怎么用思维导图做会议纪要?MindNow来教你
  2. 关于python搞笑段子精选_搞笑却有哲理的段子
  3. 信息系统安全等级保护流程
  4. 3、echarts配置项-xAxis
  5. 银行笔试用计算机,2018银行招聘笔试:如何备考银行计算机
  6. 短信验证码接口的应用场景和优势
  7. uniapp小程序展示3D模型
  8. 每周分享第 38 期
  9. 2022-3-26JavaSE试卷-答案
  10. ​信息管理系统整合XtraReport报表平台之Asp.Net Core站点CentOS部署​