更多精彩内容,欢迎关注微信公众号:tmac_lover
上一篇介绍python中字典和集中时,提到,字典和集合的优势在于增删改查非常高效,而高效的原因就在于,python中字典和集合这两个数据结构是基于散列表来实现的。散列表也叫哈希表,英文是hashtable。是一种非常高效的基础数据结构,今天我们介绍一下散列表的原理,了解了原理之后,就会很容易理解为什么字典和集合的增删改查如此高效。
散列表听上去好像很玄乎,但是实际上在我们的生活中却非常的常见。举个很简单的例子,我们读大学的时候,辅导员老师那里的excel表里有每个学生的信息,如果我们想要查找某一个学生的信息,使用什么方法最迅速呢?
一种很容易想到的方法是打开excel,然后从第一行开始一行一行的看,直接看到我们想要找的那个学生的那一行为止,可想而知,如果有5000个学生,很不巧这名学生刚好在excel表的最后一行,是不是要耗很长时间才能找到。这种方法有点类似于列表,查找的时间复杂度为O(n)。
还有另外的方法,因为每个学生都有学号,并且学号是连续的,如果我们存储的时候,直接按学号从小到大的顺序,从第一行开始存,查找的时候,只要知道学生的身份证号,是不是就能很快速的定位到该名学生在excel表格里的第几行,然后就可以直接拿到学生的信息。使用这种方式,只需要经过一次计算,就能确定学生信息的位置,而这个计算的时间是相对固定的。这就是典型的一种散列表的思想,它查找的时间复杂度是O(1)。这个计算偏移的方法,就相当于散列函数。这里学生的学号就是散列表中的键(key)。
用图表示就像下面这样:
我们只需要设计一个散列函数,然后利用这个散列函数,根据学生的学号就可以计算出每个学生信息在excel表里的存储位置。这样就可以实现快速的增删改查,效率非常的高。
以上就是散列表所包含的非常基础的思想。当然实际的散列表的实现,还要考虑的因素有很多,比如:
如果想更深入的了解散列表,可以自行查阅一些资料,在这里,我们只需要了解散列表的实现原理,以及为什么散列表的增删改查效率如此之高就可以了。
了解了上面所说的散列表的基本原理之后,我们知道,散列表中元素的实际存储位置是由所设计的散列函数对键(key)进行运算后得出的。上面所举的学号的例子比较特殊,使用的散列函数相当于只是对学号数字取了一个偏移来得到学生信息存储位置。而实际上大多数的散列函数在对键进行计算后,得到的存储位置是随机的,并不连续,所以元素的存储位置也就不一定和输入的顺序相同。
讨论这个问题前,我们需要知道字典的键必须要求是可以散列的,而一个可散列的对象必须满足以下要求:
通过下面代码测试一下:
1l = [1,2,3,4]
2print(hash(l))
运行会报如下错误:
1Traceback (most recent call last):
2 File "", line 1, in
3TypeError: unhashable type: 'list'
实际上,list并没有实现hash()这个魔法函数,因为只有不可变对象,才可以进行hash, 像字符串,字典这种都属于不可变对象,而集合,列表,字典都属于可变对象,所以它们无法作为字典的key。