散列表 / Hash Table


散列表与散列函数

散列表是一种将关键字映射到特定数组位置的一种数据结构,而将关键字映射到0至TableSize-1过程的函数,即为散列函数

Hash Table:
[0] -> A
[1] -> B
[2] -> C
[3] -> D
[4] -> E

下面以一个简单的散列函数 Hash(Key)=Key mod TableSize为例,完成一个散列表的实现。

Note: 为方便起见,这里选用了一个非素数作为TableSize,适宜的TableSize应为一个素数。

完整代码

 from collections import Iterable

 class CollisionError(Exception):
pass class HashTable:
"""
Hash Table:
[0] -> A
[1] -> B
[2] -> C
[3] -> D
[4] -> E
"""
def __init__(self, size, fn):
self._array = [None for i in range(size)]
self._hashing = fn def __str__(self):
return '\n'.join('[%d] %s' % (index, item) for index, item in enumerate(self._array)) def find(self, item):
hash_code = self._hashing(item)
value = self._array[hash_code]
return value if value == item else None, hash_code def insert(self, *args):
for i in args:
if isinstance(i, Iterable):
for j in i:
self._insert(j)
else:
self._insert(i) def _insert(self, item):
if item is None:
return
hash_code = self._hashing(item)
value = self._array[hash_code]
if value is not None and value != item: # Handle value 0 and value existed situation.
raise CollisionError('Hashing value collided!')
self._array[hash_code] = item def delete(self, item):
hash_code = self._hashing(item)
if self._array[hash_code] != item:
raise KeyError('Key error with %s' % item)
self._array[hash_code] = None def show(self):
print(self) @property
def size(self):
return len(self._array) @property
def load_factor(self):
element_num = sum(map(lambda x: 0 if x is None else 1, self._array))
return element_num/self.size def make_empty(self):
self._array = [None for i in range(self.size)] def kmt_hashing(size):
# Key = Key mod TableSize
return lambda x: x % size def test(h):
print('\nShow hash table:')
h.show() print('\nInsert values:')
h.insert(7, 8, 9)
h.insert(range(7))
h.show()
print('\nInsert values (existed):')
h.insert(1)
h.show()
print('\nInsert value (collided):')
try:
h.insert(11)
except CollisionError as e:
print(e) print('\nFind value:')
print(h.find(7))
print('\nFind value (not existed):')
print(h.find(77)) print('\nDelete value:')
h.delete(7)
h.show()
print('\nDelete value (not existed):')
try:
h.delete(111)
except KeyError as e:
print(e) print('\nLoad factor is:', h.load_factor)
print('\nClear hash table:')
h.make_empty()
h.show() if __name__ == '__main__':
test(HashTable(10, kmt_hashing(10)))

分段解释

首先导入一个可迭代类,用于判断参数类型时使用,并定义一个散列冲突异常类

 from collections import Iterable

 class CollisionError(Exception):
pass

接着定义一个散列表类,构造函数接收两个参数,一个用于设置散列表的大小,一个用于设置散列函数,

Note: 由于Python的列表无法像C语言中的数组一样提前声明大小,因此这里的列表需要先用None进行填充。

 class HashTable:
"""
Hash Table:
[0] -> A
[1] -> B
[2] -> C
[3] -> D
[4] -> E
"""
def __init__(self, size, fn):
self._array = [None for i in range(size)]
self._hashing = fn

再重载__str__方法,用于更加清晰的显示散列表,

     def __str__(self):
return '\n'.join('[%d] %s' % (index, item) for index, item in enumerate(self._array))

定义散列表的find方法,find方法的时间复杂度为O(1),查找时仅需根据键值计算哈希值,再从散列表中获取元素即可。返回查找到的结果和对应哈希值,若未找到元素则返回None和最后查找的位置。

Note: O(1)的前提是散列函数足够简单快速

     def find(self, item):
hash_code = self._hashing(item)
value = self._array[hash_code]
return value if value == item else None, hash_code

定义散列表的insert方法,首先对传入的参数进行判断,若为可迭代对象则迭代插入,否则直接插入。私有的插入方法将利用散列函数对插入值进行散列计算,然后插入对应位置,若对应位置已被占有,则引发一个冲突异常。

     def insert(self, *args):
for i in args:
if isinstance(i, Iterable):
for j in i:
self._insert(j)
else:
self._insert(i) def _insert(self, item):
if item is None:
return
hash_code = self._hashing(item)
value = self._array[hash_code]
if value is not None and value != item: # Handle value 0 and value existed situation.
raise CollisionError('Hashing value collided!')
self._array[hash_code] = item

定义散列表的delete方法,当需要删除某个值时,同样先进行散列计算,找到对应散列位置,若该位置的值与删除值不同,则引发一个键错误异常,若相同或为None,则直接删除该元素。

     def delete(self, item):
hash_code = self._hashing(item)
if self._array[hash_code] != item:
raise KeyError('Key error with %s' % item)
self._array[hash_code] = None

接着定义散列表几个基本方法,包括显示散列表,获取散列表大小,计算装填因子和清空散列表。

     def show(self):
print(self) @property
def size(self):
return len(self._array) @property
def load_factor(self):
element_num = sum(map(lambda x: 0 if x is None else 1, self._array))
return element_num/self.size def make_empty(self):
self._array = [None for i in range(self.size)]

最后,定义一个简单的散列函数Hash(Key)=Key mode TableSize。

 def kmt_hashing(size):
# Key = Key mod TableSize
return lambda x: x % size

以及一个测试函数,对散列表进行测试。

首先显示一个初始的散列表,

 def test(h):
print('\nShow hash table:')
h.show()

得到结果

Show hash table:
[0] None
[1] None
[2] None
[3] None
[4] None
[5] None
[6] None
[7] None
[8] None
[9] None

接着测试插入方法,向散列表中插入元素

     print('\nInsert values:')
h.insert(7, 8, 9)
h.insert(range(7))
h.show()

得到结果

Insert values:
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4
[5] 5
[6] 6
[7] 7
[8] 8
[9] 9

尝试插入已存在的元素,则没有影响,而尝试插入一个冲突元素,则会引发一个冲突异常

     print('\nInsert values (existed):')
h.insert(1)
h.show()
print('\nInsert value (collided):')
try:
h.insert(11)
except CollisionError as e:
print(e)

显示结果

Insert values (existed):
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4
[5] 5
[6] 6
[7] 7
[8] 8
[9] 9 Insert value (collided):
Hashing value collided!

尝试查找一个存在的元素和一个不存在的元素

     print('\nFind value:')
print(h.find(7))
print('\nFind value (not existed):')
print(h.find(77))

得到结果

Find value:
(7, 7) Find value (not existed):
(None, 7)

尝试删除一个存在元素和一个不存在的元素

     print('\nDelete value:')
h.delete(7)
h.show()
print('\nDelete value (not existed):')
try:
h.delete(111)
except KeyError as e:
print(e)

得到结果

Delete value:
[0] 0
[1] 1
[2] 2
[3] 3
[4] 4
[5] 5
[6] 6
[7] None
[8] 8
[9] 9 Delete value (not existed):
'Key error with 111'

查看装载因子,最后清空散列表

     print('\nLoad factor is:', h.load_factor)
print('\nClear hash table:')
h.make_empty()
h.show()

得到结果

Load factor is: 0.9

Clear hash table:
[0] None
[1] None
[2] None
[3] None
[4] None
[5] None
[6] None
[7] None
[8] None
[9] None

一个基本的散列表基本建立完成,但还存在一个插入冲突的问题没有解决,对于插入冲突现象,解决的方式主要有分离链接法开放定址法,具体内容可参考相关阅读。

相关阅读


1. 分离链接法

2. 开放定址法

Python与数据结构[4] -> 散列表[0] -> 散列表与散列函数的 Python 实现的更多相关文章

  1. Python与数据结构[3] -> 树/Tree[0] -> 二叉树及遍历二叉树的 Python 实现

    二叉树 / Binary Tree 二叉树是树结构的一种,但二叉树的每一个节点都最多只能有两个子节点. Binary Tree: 00 |_____ | | 00 00 |__ |__ | | | | ...

  2. Python与数据结构[1] -> 栈/Stack[0] -> 链表栈与数组栈的 Python 实现

    栈 / Stack 目录 链表栈 数组栈 栈是一种基本的线性数据结构(先入后出FILO),在 C 语言中有链表和数组两种实现方式,下面用 Python 对这两种栈进行实现. 1 链表栈 链表栈是以单链 ...

  3. Python与数据结构[2] -> 队列/Queue[0] -> 数组队列的 Python 实现

    队列 / Queue 数组队列 数组队列是队列基于数组的一种实现,其实现类似于数组栈,是一种FIFO的线性数据结构. Queue: <--| 1 | 2 | 3 | 4 | 5 |<-- ...

  4. Python与数据结构[4] -> 散列表[2] -> 开放定址法与再散列的 Python 实现

     开放定址散列法和再散列 目录 开放定址法 再散列 代码实现 1 开放定址散列法 前面利用分离链接法解决了散列表插入冲突的问题,而除了分离链接法外,还可以使用开放定址法来解决散列表的冲突问题. 开放定 ...

  5. python对redis的常用操作 上 (对列表、字符串、散列结构操作)

    这里的一切讨论均基于python的redis-py库. 安装使用: pip install redis 然后去获取一个redis客户端: redis_conn = redis.Redis(host=R ...

  6. python --整理数据结构(列表)

    该整理来源于:https://www.runoob.com/python3/python3-data-structure.html 列表 python中列表是可变的,这是它区别于字符串和元组的最重要的 ...

  7. Python常见数据结构--列表

       列表 Python有6个序列的内置类型,但最常见的是列表和元组. 序列都可以进行的操作包括索引,切片.加.乘.检查成员. 此外,Python已经内置确定序列的长度以及确定最大和最下的元素的方法. ...

  8. (python)数据结构------列表

    一.数字的处理函数 (一)int() 取整数部分,与正负号无关,举例如下: print(int(-3.6), int(-2.5), int(-1.4)) print(int(3.6), int(2.5 ...

  9. Oracle表分区分为四种:范围分区,散列分区,列表分区和复合分区(转载)

    一:范围分区 就是根据数据库表中某一字段的值的范围来划分分区,例如: 1 create table graderecord 2 ( 3 sno varchar2(10), 4 sname varcha ...

随机推荐

  1. 周记【距gdoi:91天】

    这星期挺没状态的.听蔡大神讲组合游戏,然后欢乐得以为自己懂了,然后看到题目就懵了,然后就各种乱各种走神……但是某大神们(kpm和child)疯狂地切题.然后又颓废了两个晚上后决定滚回去文化课(oi没状 ...

  2. MySQL使用笔记(四)数据的操作

    By francis_hao    Dec 14,2016 数据的操作包括插入数据记录.更新数据记录和删除数据记录. 插入数据记录 插入单条数据记录 field表示的字段名和value表示数据要一一对 ...

  3. Spring securiuty 过滤器

    1. HttpSessionContextIntegrationFilter 位于过滤器顶端,第一个起作用的过滤器. 用途一,在执行其他过滤器之前,率先判断用户的session中是否已经存在一个Sec ...

  4. 用户线程 (User Thread)、守护线程 (Daemon Thread)

    在Java中有两类线程:用户线程 (User Thread).守护线程 (Daemon Thread). 所谓守护 线程,是指在程序运行的时候在后台提供一种通用服务的线程,比如垃圾回收线程就是一个很称 ...

  5. noip2016 提高组

    T1 玩具谜题 题目传送门 这道题直接模拟就好了哇 233 #include<cstdio> #include<cstring> #include<algorithm&g ...

  6. Linux提权思路

    先写个大概 0. dirtycow 不同的dirtycow有不同执行条件. 使用前先对照此表根据内核版本确认是否已经修复 https://github.com/dirtycow/dirtycow.gi ...

  7. GridPanel分页条插件

    GridPanel的分页条没有设置当前页显示条数的功能,会不大方便 主要是抄袭的http://www.cnblogs.com/badwps/archive/2011/04/15/2016440.htm ...

  8. github 下载某一文件夹

    作者:知乎用户链接:https://www.zhihu.com/question/25369412/answer/96174755来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注 ...

  9. linux基础的基础命令操作

    一.开启Linux操作系统,要求以root用户登录GNOME图形界面,语言支持选择为汉语 操作:su - root 二.使用快捷键切换到虚拟终端2,使用普通用户身份登录,查看系统提示符 操作:ctrl ...

  10. shell浅谈之三for、while、until循环【转】

    转自:http://blog.csdn.net/taiyang1987912/article/details/38929069 版权声明:本文为博主原创文章,未经博主允许不得转载.   目录(?)[- ...