hash哈希(hash)是计算机科学中一种处理数据的方法,会通过特定的函数/算法(称为哈希函数/算法)将检索到的项与一个索引(称为哈希或哈希值)用于检索,从而产生易于搜索的数据结构(称为哈希表)。什么是哈希表?哈希表(hashtable)是一种根据键(Key)直接访问内存存储位置的数据结构。根据键(Key)值将数据映射到内存中某个位置的函数称为哈希函数,根据哈希函数记录数据的表称为哈希表。哈希表的特点如果键是,它的值存储在存储位置。这样,可以直接得到被检查的记录,而不需要进行比较。这种对应关系称为散列函数,按照这种思想构建的表称为散列表。不同的关键字可能得到相同的哈希地址,即和,这种现象称为冲突。如果对于关键字集合中的任意一个关键字,被哈希函数映射到地址集合中任意一个地址的概率都相等,那么这种哈希函数就称为统一哈希函数(UniformHashfunction),意思是它是通过哈希函数使关键字获得一个“随机地址”,从而减少冲突。处理冲突开放式寻址法开放式寻址法是在发生冲突后寻找下一个空闲空间。函数定义为:其中hash(key)为哈希函数,为递增序列,为碰撞次数。线性探测:,或其他线性函数。相当于一个一个地检测存储地址的表,直到找到一个空单元,然后放入单元中。正方形检测方法:链表法这是另一种冲突解决方法。散列到同一个位置的元素不再继续检测,而是在这个位置是一个链表,这些元素都放在这个链表中。优越的。如果rehashing不够,再做一遍,直到冲突不再发生。建立一个普通的溢出区,将哈希表分为两部分,基本表和溢出表。所有与基本表冲突的元素将被填充到溢出表中(注意:在这种方法中,元素存储在两个单独的表中)。哈希表的应用查字典在网络防火墙中,根据源IP、目的IP、源端口、目的端口、协议号组成的五元组来识别网络数据流,会话表项是根据五元组建立(sessionentry)。为了方便查找,一般采用Hash表来实现这个会话表,以提高转发效率。Linux内核使用了大量的哈希表,利用开放地址法(线性检测)实现了一个简单的哈希表。仔细阅读后,尝试哈希表的各种操作,感受下哈希表的飞速发展吧!代码段1classHash:#表的长度位于11def__init__(self):self.hash_table=[[None,None]foriinrange(11)]#哈希函数defhash(self,k,i):h_value=(k+i)%11ifself.hash_table[h_value][0]==k:returnh_valueifself.hash_table[h_value][0]!=None:i+=1ih_value=self.hash(k)返回h_valuedefput(self,k,v):hash_v=self.hash(k,0)self.hash_table[hash_v][0]=kself.hash_table[hash_v][1]=vdefget(self,k):hash_v=self.hash(k,0)returnself.hash_table[hash_v][1]hash=Hash()hash.put(1,'wang')print(hash.get(1))上面的代码实现我创建了一个简单的哈希表,但是表的长度只有11,表中填充的元素越来越多后,冲突的可能性就会增加。这就引出了另一个概念,叫做负载因子(loadfactor)。负载因子的定义是:所以当达到一定程度时,表的长度会发生变化,负载因子设计为0.75;如果超过0.8,CPU的cachemissing会急剧上升。即需要定义一个新的resize函数来扩展表的长度,一般选择扩展到插入元素个数的两倍。重新升级上面代码中我们的Hash!示例代码在片段2中。代码段2classMap:def__init__(self):self.capacity=11self.hash_table=[[None,None]foriinrange(self.capacity)]self.num=0self.load_factor=0.75selfdefhash(,k,i):h_value=(k+i)%self.capacityifself.hash_table[h_value][0]==k:返回h_valueifself.hash_table[h_value][0]!=None:i+=1h_value=self.hash(k,i)returnh_valuedefresize(self):#扩展到原来有元素数量的两倍self.capacity=self.num*2temp=self.hash_table[:]=self[.hash_table[:]=self[.hash[None,None]foriinrange(self.capacity)]foriintemp:#把原来有的元素存入if(i[0]!=None):hash_v=self.hash(i[0]hash(,0)self.hash_table[hash_v][0]=i[0]self.hash_table[hash_v][1]=i[1]defput(self,k,v):hash_v=self.hash(k,0)self.hash_table[hash_v][0]=kself.hash_table[hash_v][1]=v#暂不考虑密钥重复的情况,具体自己可以优化self.num+=1#如果比例大于加载因子if(self.num/len(self.hash_table)>self.load_factor):self.resize()defget(self,k):hash_v=self.hash(k,0)returnself.hash_table[hash_v][1]经典实践Python中的字典是通过哈希表实现的,其特点如下:字典的每个键值key=>value对是以冒号分隔:,每个键值对以逗号分隔,整个字典包含在花括号{}中,格式:dict={key1:value1,key2:value2}通过访问、添加和更新元素方括号dictionary={'name':'wang','age':17,'class':'first'}dictionary['age']=18dictionary['country']='china'print(dictionary['age'])print(dictionary['country'])根据Python中的字典特性,手动实现一个Python字典classMyDictionary(object):#字典类初始化def__init__(self):self.table_size=13#Hashtablesizeself.key_list=[无]*self.table_size#存放key的Listself.value_list=[None]*self.table_size#存放value的List#哈希函数,返回哈希值#key为要计算的keydefhashfuction(self,key):count_char=0key_string=str(key)forkey_charinkey_string:#计算key中所有字符的ASCII值Sumcount_char+=ord(key_char)#ord()函数用于求ASCII值length=len(str(count_char))iflength>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_charreturnmid_int%self.table_size#取余数作为哈希值返回给它Rehashfunction#,返回新的哈希值#hash_value是旧的哈希值defrehash(self,hash_value):return(hash_value+3)%self.table_size#线性检测用aforwardintervalof3#storekey-valuepairsdef__setitem__(self,key,value):hash_value=self.hashfuction(key)#计算哈希值ifNone==self.key_list[hash_value]:#如果哈希值为空,可以放置键值对传递elifkey==self.key_list[hash_value]:#哈希值不为空,旧键值对的键值与新键值对的键值相同,作为更新,可以放置键值对passelse:#hash值不为空,key值也不同,即发生“碰撞”,然后使用re-hash函数继续检测,直到找到空位为止hash_value=self.rehash(hash_value)#Rehashwhile(None!=self.key_list[hash_value])and(key!=self.key_list[hash_value]):#仍然不能插入键值对,rehashhash_value=self.rehash(hash_value)#Rehash#放置键值对self.key_list[hash_value]=keyself.value_list[hash_value]=value#根据key获取valuedef__getitem__(self,key):uctionh.valuefashself_key)#计算哈希值first_hash=hash_value#记录初始哈希值作为重新哈希检测的停止条件ifNone==self.key_list[hash_value]:#如果哈希值为空,则key不存在valuepairreturnNoneelifkey==self.key_list[hash_value]:#hash值不为空,key值与正在查找的key值相同,则返回对应的值returnself.value_list[hash_value]else:#hash值不为空,且key值也不同,即发生“碰撞”,则使用re-hash函数继续检测,直到出现空位或者找到相同的键值hash_value=self.rehash(hash_value)#重新哈希while(None!=self.key_list[hash_value])and(key!=self.key_list[hash_value]):#还是没有找到,重新-hashhash_value=self.rehash(hash_value)#重新哈希ifhash_value==first_hash:#哈希值检测返回到起点,判断找不到returnNone#while循环结束表示找到空缺或相同的listkeyvalueone_key==selfN[hash_value]:#如果哈希值为空,则键值对不存在returnNoneelse:#哈希值不为空,键值与搜索中的键值相同,然后返回对应的值returnself.value_list[hash_value]#删除键值对def__delitem__(self,key):hash_value=self.hashfuction(key)#先计算哈希值_hash=hash_value#记录初始哈希值作为停止rehashingdetectionconditionforrehashingdetectionifNone==self.key_list[hash_value]:#如果hash值为空,键值对不存在,不需要删除returnelifkey==self.key_list[hash_value]:#Thehash值不为空,如果key值与正在查找的key值相同,删除self.key_list[hash_value]=Noneself.value_list[hash_value]=Nonereturnreturnelse:#hash值不为空,key值也不同,即发生“冲突”,使用re-hash函数继续检测,直到空缺或找到相同键值hash_value=self.rehash(hash_value)#重新哈希while(None!=self.key_list[hash_value])and(key!=self.钥匙_list[hash_value]):#还是没有找到,重新哈希hash_value=self.rehash(hash_value)#重新哈希ifhash_value==first_hash:#哈希值检测回到起点,判断找不到urntreendwhile循环的意思是找到一个空位或者相同的key值ifNone==self.key_list[hash_value]:#hash值为空,则没有这个key-value对returnreturnelse:#哈希值不为空,键值与查找中的键值相同,则删除self.Key_list[hash_value]=noneself.value_list[have_value]=none返回#字典的长度def__Len__(Self):Count=0forkey.:ifkey!=None:count+=1returncountdefmain():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("key%s的值为%s"%("kmonkey",H["kmonkey"]))print("字典的长度是%d"%len(H))delH["klion"]print("字典的长度是%d"%len(H))print(H.key_list)print(H.value_list)if__name__=="__main__":main()最近花了几天时间整理了一个Python入门的理论+实践进阶教程,这个可能是你看过的非常棒的一本学习资料,独家打造,完全免费,需要的同学可以关注gzh【Python编程学习圈】,发送“学习资料”获取~
