哈希表的填充因子,哈希表的平均查找长度与装填因子

  哈希表的填充因子,哈希表的平均查找长度与装填因子

  1.普通夜间功能

  在前面的部分中,有一个暗夜函数用于两次探索。

  问题:暗夜功能怎么选?

  当然哈希得到的碰撞越小越好,也就是每个键可以尽量哈希到M个槽中的任意一个,和其他键哈希到哪个槽无关。(统一哈希)

  2.负载系数(负载系数)

  值=已使用的插槽数量/插槽总数。

  当我们一直往平淡的床头柜里插数据的时候,空间很快就不够用了,然后根据加载因子扩充容量。

  比如在插入8个元素之前,槽的总数是13,那么它的加载因子是0.62;如果继续插入,很快槽数就不够了。当装载系数超过0.8时,我们必须重新打开空间,并执行“沉重的黑暗之夜”操作。

  3.重复沉闷的夜晚

  流程:一、开辟新空间。比如Cpython的实现就有自己的策略。和之前槽位总长度相比,用什么策略增长?

  我们先来看看Cpython解释器的代码:

  在dictobject.c的文件中,他有关键字GROWTH_RATE。在不同版本的python中,值是不同的,不同版本的cpython使用不同的策略。

  (1)在3.3.0中,他的值是取‘已用槽数’x2;

  (2)在3.2中,他的增加是取‘已用槽数’x4;

  (3)当前使用的(3.5)是取(使用的槽数 x2)(容量值/2)

  以上三点都是重暗夜的扩张策略。

  其实暗夜就是先扩充容量,扩充完之后把之前的值都放到新的暗夜表里重新插入。

  表示在连续插入数据的过程中,一旦加载因子超过规定值,比如之前的0.8,就会进行暗夜操作。

  以下是通过代码模拟实现的(实际上是通过模拟cpython来实现沉闷的守夜)

  [

  注意:床头柜阵列的普通插槽,一个插槽有3种状态:

  它们是:

  从未用过或抵触过;

  用过,但去掉;

  该插槽正在被占用;

  ]

  这里,复制前面的Array()类,并做一些小的改动:

  定义数组类 class array(objece):def _ _ init _ _(self,size=32,init=none):# Change:init参数self。_ size=sizeself。_ items=[init]* size def _ _ getitem _ _(self,index):returnself。_ items[index]def _ _ setitem _ _(self,index):self。_ items[index]=valuedef _ _ len _ _(self):return self。_sizedefclear(self,Value=none): foriinrange (self。_ items):自我。_ items[I]=valuedef _ _ ITER _ _(self):for items self . items:YieldItem 定义床头柜数组的flat slot classSlot(object)。注意:一个插槽有三种状态。与解决冲突的链接方法相比,第二种探测方法删除一个键的操作稍微复杂一些。 DEF _ _ init _ _ (self,key,value): self.key,self.value=key,value 定义了一个沉闷的夜桌 类hashtable (object): unused=none #表示Slot从未被使用过。EMPTY=Slot(None,None)#表示尽管Slot已被使用,但它已被删除。DEF _ _ init _ _ (self): self。_ table=array (8,Init=HashTable。未使用)#8表示长度数组(2的n次方),而#HashTable。未使用表示尚未使用。初始化数组的每个元素。 Self.length=0#表示已使用的插槽数量。定义加载因子 @ propertydef _ load _ factor (self):返回self.length/float(len(self。_ table)#取使用槽数/平淡夜表总长度def _ _ len _(self):return self . length #直接返回平淡夜表的长度定义平淡夜函数 def_hash(self,key): 根据key获取一个数组的下标。这里简化一下,用内置的暗夜函数得到一个整数值,取其绝对值,然后直接对数组长度取模。 返回ABS (hash (key)% len (self。_ table)“”定义沉闷夜桌的常用操作“”def_find_key(self,key):# Role:找槽,找键位置index=self。_hash(key)#调用hash函数获取第一个slot的位置_len=len(self。_table)#定义hash函数的长度。_ table [index]不是哈希表。未使用:#当该槽不是未使用槽时,将继续向下查找ifself。_ table[index]ishashtable . empty:#如果此槽已被使用并且值已被删除,则它当前为空。index=(index*5 1)%_len#模拟冲突解决策略,直接使用平原之夜的方式,#这是Cpython中使用的一种解决平原之夜冲突的方式。继续自我。_ table [index]。key==key: #如果这个槽被占用了,并且他的键和你要找的键一致,returnindex#直接返回当前下标else:index=((index*5) 1)%_len#否则直接寻找下一个槽的位置。ReturnNone#如果什么都没有找到,ReturnNone 实现一个辅助方法 def_slot_can_insert(self,Index):#判断是否可以用新值return (self)插入插槽。_ table[index]ishashtable . emptyorselff . _ table[index]ishashtable . unused)#确定槽是空的还是未使用的定义寻找插入新值的槽 def_find_slot_for_insert(self,key):index=self。_hash(key)_len=len(self。_table)whilenotself。_ slot _ can _ insert(index):index=(index * 5 ^ 1)% _ lenreturnindex“”实现了一个in运算符“”def _ _ contains _ _ (self,key): index=self。_ find _ key (key) #首先查找keyreturnindexitnone #表示已经找到常用方法 defadd(self,key) Value):ifkeyinself:#如果键在槽中index=self。_find_key(key)#先自己找到这把钥匙的位置。_ table [index]。value=value # Update slot的值returnFalse#表示尚未执行插入操作,而是更新操作else: index=self。_ find _ slot _ for _ insert (key) #找到插槽位置并插入。自我。_ table [index]=slot (key,value) #然后将该值分配给新的slot。这个槽的值是(key,value)self.length=1#如果self,这个槽使用的长度是1。

  自我returnTruedef _ rehash(self):old _ table=self ._表格编号将原来的平淡的黑夜数组保存到旧_表格里面接下来要开辟一个新长度的新数组,就以简单的扩容策略来写,把原来的长度乘以2newsize=len(self ._table)*2#扩容策略也比较简单*2就行了自我. table=Array(newsize,HashTable .未使用)#给自我。_表格赋新值self.length=0#将长度归0forslotinold_table:#遍历旧的,将旧的全部插到新的里面去ifslotisnotHashTable .UNUSEDandslotisnotHashTable。空:#满足这两个条件后指数=自我. find_slot_for_insert(slot,key)#先去调用_查找_插槽_for_insert给它找个位置去插入自我. table[index]=slot#给这个槽赋值self.length=1#将长度递增defget(self,key,default=None):#定义得到操作指数=自我. find_key(key)#先查找值是不是秀丽的棒球ifindexsnone:#如果不秀丽的棒球returndefaultelse:returnself ._ table[索引].价值#否则返回这个位置的valuedefremove(self,key):#定义删除操作指数=自我. find _ key(key)ifindexsnone:#判断一下是否找到钥匙,如果没找到raiseKeyError()#返回keyerrorvalue=self ._ table[索引].值)#否则将这个要删除的值取出来self.length-=1#长度减一自己. table[index]=哈希表。空#将当前位置赋值给空槽返回值#返回这个被删除的值def__iter__(self):#迭代知道大蟒的字典是遍历他的钥匙,所以这里也是用同样的方式实现为了自己表: 若果这个槽不是空和未被使用的槽里面的话" " ifslotisnotin(哈希表EMPTYandHashTable。未使用):让步槽。按键# # #单元测试# # # # def test _ hash _ table():h=HashTable()h . add( a ,0)h.add(b ,1)h.add(c ,2)assertlen(h)==3 asserth。get( a )==1 asserth。get( b )==1 asserth。get( c )==2 asserth。get( asdf )是noneh。删除( a )asserth。get( a )是非assertsorted(list(h))==[ b ,按键来进行排序对于iinrange(n):h,n=50。get(I)==I

  定义了素夜表之后,后面实现字典和set set就比较容易了。

  转载于:https://blog.51cto.com/286577399/2345207

哈希表的填充因子,哈希表的平均查找长度与装填因子