python算法--哈希表原理与实现

散列表简述

这是一种查找效率为O(1)的结构,又称之为散列hashing;

存放数据的这种结构,有槽号;存放数据时建立槽号和数据的映射关系,然后查找时,用同样的哈希函数去计算出槽号,看槽号里有没有就可以了。如,求余就是一个好的散列函数。

关键问题一:散列函数中碰到冲突怎么办?

所以我们希望有一种完美的哈希函数,能够没有冲突,显然在有限的输出里是不现实的–所以我们只能尽量创造出比较好的函数来让冲突尽可能的小。

常见有MD5,SHA0 , SHA1 , SHA256 , SHA224 , SHA384 , SHA512 .其中MD5是128位,16个字节。SHA0/SHA1是输出的160位(20个字节)。

Map数据结构实现

Map实现的是用包含着一种 键-值 匹配的结构 对象,就像是字典一样;

其中核心的功能是实现通过快速找到某个键,从而得到它的值;所以为了实现0(1)的算法,我们可以采用散列表;

下面是一种最简单的实现散列表的方式:

class HashTable:
    def __init__(self):
        self.size=11
        self.slots=[None]*self.size
        self.data=[None]*self.size

    def hashfunction(self,key):#根据key值选一个位置
        pass

    def rehash(self,hashvalue):#如果第一次没找到合适的,用rehash函数再找下一个可用的位置
        pass

    def put(self,key,value):
        hashvalue=self.hashfunction(key)
        if self.slots[hashvalue]==None or self.slots[hashvalue]==key:
            self.slots[hashvalue]=key
            self.data[hashvalue]=value
        else:
            nextslot=self.rehash(hashvlaue)
            while self.slots[nextslot]!=None and self.slots[nextslot]!=key:
                nextslot=self.rehash(nextslot)

            self.slots[nextslot]=key
            self.data[nextslot]=value

    def get(self,key):
        pass
    

审视整个实现代码,散列表的核心代码有两个–put函数和get函数。put函数具有key和value参数,用来将一对 键-值放进结构里,get函数就是通过key去选择相应的value,如果没有的话返回false,或者爆出异常。

实现思路:

通过两个同样大小的列表来实现槽和值,用下标数来做对应关系:

散列表的存放原则使用hashfunction和rehash函数,来通过key算出下标;

处理冲突的方法:如果碰到冲突了,就后延,后延几个,怎么算。由rehash决定。

比较关键的get函数实现

    def get(self,key):
        hashvalue=self.hashfunction(key)
        
        if self.slots[hashvalue]==key:
            return self.data[hashvalue]
        else:
            times=1
            nextslot=self.rehash(hashvalue)
            while self.slots[nextslot]!=None and self.slots[nextslot]!=key and times<12:
                nextslot=self.rehash(nextslot)
                times+=1
            
            if self.slots[nextslot]==None or times>12:
                raise NotExist
            else:
                return self.data[nextslot]

get函数的实现思路和put函数差不多,思路是反过来的:先同样用hashfunction去计算,如果不是对应的key值,则再rehash一下。

:在get函数中,考虑了结构全部满了都没有key值的情况,用times来判断,其实在put函数里也存在这种情况,只是没写。 但其实可以在更加源头处解决put函数的情况,就是put时判断下结构体存储数值是否已经满了。这里的12是self.size

简单的哈希函数和再哈希的实现

    def hashfunction(self,key):#根据key值选一个位置
        return key%self.size

    def rehash(self,hashvalue):#如果第一次没找到合适的,用rehash函数再找下一个可用的位置
        return (hashvalue+1)%self.size

这里用最常用也是最简单的一种哈希函数–取余;rehash的方法就是直接再+1继续取余就行。

其他细节

1、不简化的put

这里的put函数其实经过了我的简化,就是碰到None或者碰到key值,都使用同样的操作,这是为了简化代码方便理解。在碰到key时,直接更新value值就可以,这里客气区分开来:

    def put(self,key,value):
        hashvalue=self.hashfunction(key)

        if self.slots[hashvalue]==None:
            self.slots[hashvalue]=key
            self.data[hashvalue]=value
        else:
            if self.slots[hashvalue]==key:
                self.data[hashvalue]=value
            else:
                nextslot=self.rehash(hashvalue)
                while self.slots[nextslot]!=None and self.slots[nextslot]!=key:
                    nextslot = self.rehash(nextslot)

                if self.slots[nextslot]==None:
                    self.slots[nextslot]=key
                    self.data[nextslot]=value
                else:
                    self.data[nextslot]=value

如上就是不简化的完整代码。

2、用[]直接表示结构体的存取

对于我们新建的这个哈希表:

hslist=HashTable(10)
hslist.put(3,'wa')
print(hslist.slots)
print(hslist.data)
print(hslist.get(3))

一般使用这样的方法去调用,但是略显麻烦,定义一个键值的数据结构,我们往往希望能够更加直观的去表示,比如如下这样:

hs=HashTable(11)
hs[103]='wa'
print(hs[103])

如果想要这样是,只要在类里再加上相应的特殊函数就可以了。

    def __getitem__(self, item):
        return self.get(item)
    
    def __setitem__(self, key, value):
        return self.put(key,value)

这里的魔术方法就可以让结构体直接使用[]去进行调用,非常的方便。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值