Python实现平方探测法散列表

本文介绍了如何使用Python实现平方探测法处理散列表中的哈希冲突,详细讲解了开放定址法中的平方探测法,包括装载因子、再散列、哈希函数和冲突解决策略。代码示例基于《数据结构与算法分析c语言描述第二版》,并讨论了表大小为素数的重要性。
摘要由CSDN通过智能技术生成

Python实现平方探测法散列表

在学习数据结构的时候,因为在网上一时间没有找到相应语言的版本,再加上我囫囵吞枣的看书方式,导致我在写平方探测法时格外困难,分享出来以加深记忆,并且为想要参考的萌新们指条明路。

这篇博客的代码翻译主要来自《数据结构与算法分析c语言描述第二版》,python编写的时候除了python的面向对象的写法和一些魔法方法没有按照原书中来,其主要算法思想来自书中。

开放定址法(Open address hashing)

通过开放定址法解决散列表的哈希冲突,一共分为三种方法,1. 线性探测法 2.平方探测法 3.双散列这三种方法。这篇博客主要来讲第2种方法的具体实现.

在开始之前,我们首先需要引入一个定义来描述散列表表内元素和表大小的比值,装填因子,装填因子主要影响我们在插入时所需要循环的次数,也可以让我们及时对表进行扩充以提高效率。

  • 我们将填充因子表示为λ,我们在进行散列查找时,可以对我们进行再散列有一个正确的评估。
  • 填充因子λ计算代码(其主要用途为在进行插入时进行判断是否需要进行再散列
   def loadFactor(self):
       return self.__size / self.__capacity

再散列的实现:

    def reHash(self):
        OldTable: list = self.__table
        newcapacity: int = self.NextPrime(self.__capacity * 2)
        self.__capacity = newcapacity
        self.__table = []
        self.__table = [HashEntry("", "") for _ in range(self.__capacity)]
        for i in OldTable:
            if i.Status == Status.Exist:
                self.insert(i.key, i.value)

平方探测法

顾名思义,在平方探测法的散列表中,单元hi (x)(i [0,n]) 其中hi(x) = (hash(x) + F(i)) mode capacity

  • 这里的capacity指的是我们初始化时所分配给列表(数组)的单元个数。
  • hash(x) 是我们通过哈希函数算得得哈希值
  • F(i)时所选择相应的解决哈希冲突的冲突函数冲突解决办法 像线性探测的哈希冲突为F(i)=i 而平方探测的冲突函数同理为F(i)= i^2

定理:使用平方探测,且表的大小为素数,那么至少有一半是空的时候,总能够插入一个新的元素

通过定理,我们需要在进行初始化的时候将表的大小尽量定义为素数(质数,顺便帮忘记的人补充一下,素数指的是除了1和它本身,其他自然数无法整除的数)。但如果当我们定义不为质数,那么在进行平方探测时,在还未将整个表填满一半之前,就无法保证在找到一个空单元了。

因此,在我们进行初始化的时候,应该通过程序,即使我们输入的不是素数,也可以计算为一个新的质数并进行初始化。具体代码如下

    def IsPrime(self, num: int) -> bool:
        '''prime number judgement'''
        Max = pow(num, 0.5)
        for i in range(2, int(Max) + 1):
            if num % i == 0:
                return False
        return True

    def NextPrime(self, num: int):
        '''calculate next prime'''
        i: int = num
        while not self.IsPrime(i):
            i += 1
        return i

这边我只是用最简单的方法进行判断是否为素数并且重新计算一个新的素数,且算法不为最优。是否使用自行斟酌。

然后,我们将所需要的元素插入相应的单元时,我们需要计算哈希值,相应的哈希算法为
∑ i = 0 K e y s i z e − 1 K e y [ K e y S i z e − i − 1 ] ∗ 3 2 i \sum_{i=0}^{Keysize-1}Key[KeySize-i-1]*32^{i} i=0Keysize1Key[KeySizei1]32i
即,相应的代码为:

	'''此处的key值类型根据需要更改'''
    def _Hash(self, key: str):
        '''calculate hash value'''
        HashVal: int = 0
        for i in key:
            HashVal = (HashVal << 5) + ord(i)
        return HashVal

    def _hash(self, key: str):
        '''mode hash value'''
        return self._Hash(key) % self.__capacity

接下来我们最后就是通过冲突函数解决哈希冲突问题,我们说过通过采用平方探测的冲突函数为F(i)=i^2
但在代码中的实现并不理想,所以根据原函数可知F(i)=F(i-1) + i * 2 - 1这个方法也是我们在进行find查找操作时主要的算法。
find方法的pthon实现代码:

 def find(self, key: str) -> int:
        '''find the key position'''
        currentPos = self._hash(key)	#哈希值所得结果
        collisionNum = 0	
        while self.__table[currentPos].Status != Status.Empty and self.__table[currentPos].key != key:
            collisionNum += 1
            currentPos = (currentPos + 2 * collisionNum - 1) % self.__capacity  #calculate position   F(i) = F(i-1) + 2 * di -1
        return currentPos

通过函数直接去看可能有些抽象,但好在这个函数足够简单,我们通过手算或画图也可以轻松算出来。
大概示意图
平方探测的冲突函数运算和插入的大概示意图。

全部代码

from enum import Enum


class Status(Enum):
    '''enum element'''
    Empty = 0
    Exist = 1
    Delete = 2


class HashEntry:
    def __init__(self, key: str, value: str):
        self.key: str = key
        self.value: str = value
        self.Status: Status = Status.Empty


class HashMap:
    def __init__(self, capacity: int):
        '''initialize'''
        self.__capacity: int = self.NextPrime(capacity)
        self.__size: int = 0
        self.__table = [HashEntry("", "") for _ in range(self.__capacity)]

    def loadFactor(self):
        return self.__size / self.__capacity

    def IsPrime(self, num: int) -> bool:
        '''prime number judgement'''
        Max = pow(num, 0.5)
        for i in range(2, int(Max) + 1):
            if num % i == 0:
                return False
        return True

    def NextPrime(self, num: int):
        '''calculate next prime'''
        i: int = num
        while not self.IsPrime(i):
            i += 1
        return i

    def _Hash(self, key: str):
        '''calculate hash value'''
        HashVal: int = 0
        for i in key:
            HashVal = (HashVal << 5) + ord(i)
        return HashVal

    def _hash(self, key: str):
        '''mode hash value'''
        return self._Hash(key) % self.__capacity

    def reHash(self):
        OldTable: list = self.__table
        newcapacity: int = self.NextPrime(self.__capacity * 2)
        self.__capacity = newcapacity
        self.__table = []
        self.__table = [HashEntry("", "") for _ in range(self.__capacity)]
        for i in OldTable:
            if i.Status == Status.Exist:
                self.insert(i.key, i.value)

    def find(self, key: str) -> int:
        '''find the key position'''
        currentPos = self._hash(key)
        collisionNum = 0
        while self.__table[currentPos].Status != Status.Empty and self.__table[currentPos].key != key:
            collisionNum += 1
            currentPos = (currentPos + 2 * collisionNum - 1) % self.__capacity  #calculate position   F(i) = F(i-1) + 2 * di -1
        return currentPos

    def insert(self, key: str, value: str) -> None:
        '''insert Key/value'''
        Pos = self.find(key)
        if self.__table[Pos].Status != Status.Exist:
            self.__table[Pos].Status = Status.Exist
            self.__table[Pos].key = key
            self.__table[Pos].value = value
            self.__size += 1
        if self.loadFactor() >= 0.5:
            self.reHash()


    def __str__(self):
        l = []
        ans = []
        m: str = ":"
        res: str = ","
        for entry in self.__table:
            if entry.Status != Status.Empty:
                l.append(entry.key)
                l.append(entry.value)
                ans.append(m.join(l))
                l = []
        return res.join(ans)
        
    def __len__(self):
        return self.__size




if __name__ == '__main__':
    '''test'''
    hp = HashMap(6)
    hp.insert("Koth", "100")
    hp.insert("Subv", "352")
    hp.insert("Kot", "100")
    hp.insert("Ko", "100")
    hp.insert("Su", "352")
    hp.insert("K", "100")
    hp.insert("Subr", "352")
	print(hp)

第一次发博客,如果任何错误,欢迎指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值