【Python数据结构与算法】哈希表

2018-7-23 回家啦,呆上两周回去~bj就剩大毛毛啦,金可怜~

在说哈希表之前需要先理清下之前各种数据结构,来看看他们的特点。

Array 数组
线性结构——内存是连续的
python自带的array模块,只能存储同一类型,且是基本的数值和字符。

List 列表——[1, 2, 3, a]
线性结构
给每一个元素都会分配一个index索引,数据项可以是不同的类型。
可以用List实现一个固定长度,并支持所有python数据类型的数组Array。

LinkedList 单链表
链式结构——内存是不连续,是一个个串起来的,因此需要指针指向下一个node节点。
一个节点node包括一个指针next,保存下一个node 的位置,还需要一个value属性保存值。
append操作是O(1),但是find、remove是O(n)的,remove需要先查找,但是单链表的查找需要从头到尾,查找到了才退出,所以时间复杂度高。

CircularDoubleLinkedList 循环双链表
比单链表多一个prev指针。需要删除一个节点node时,只需要把它前后的节点互指即可,这样子时间复杂度为O(1)。
Emmmm如果删除的是一个值,还是得遍历找出值在哪个节点,这是一个缺点。

Double Ended Queue 双端队列
队列是先进先出结构——FIFO
即是队列需要从头删除,向尾部增加元素。
可以通过上述的几种数据结构实现。
LinkedList的popleft方法和append方法都是O(1)的。

Stack 栈
栈是先进后出结构——LIFO
像是向柱捅里放东西一样,放满了,只能从最上面一个一个拿出来。
只需要一种数据结构能够方便的从尾部增减元素即可——CDLL

哈希表

前面说到循环双端链表remove操作时间复杂度是O(1),但是find操作的时间复杂度是O(n)。
而array可以通过index进行find,而删除中间元素需要移动其他元素。

哈希表就是为了解决这个问题出现的。
给数组中每个元素一个逻辑下标,从而快速找到。

它通过一个哈希函数来计算一个元素应该放在数组哪个位置,当然对于一个 特定的元素,哈希函数每次计算的下标必须要一样才可以,而且范围不能超过给定的数组长度。

可以把一段长度,每个点想成一个标着序号的槽。通过哈希函数来对每个元素进行映射分配。
但是当不同元素映射结果相同,即槽的序号相同时,也就是会出现哈希冲突

解决方法
1.链接法——chainning
让发生冲突的每个槽都变成一个链式结构
但是哈希函数不好的时候,会导致一个槽的链变得太长,使查找变慢
2.开放寻址法——open addressing
当发生collisin时,通过方法寻找下一个可用的槽。
这里根据查找下一个槽的方式不同,分为:
线性探查(linear probing): 当一个槽被占用,找下一个可用的槽
h(k,i)=(h′(k)+i)%m,i=0,1,…,m−1
二次探查(quadratic probing): 当一个槽被占用,以二次方作为偏移量
h(k,i)=(h′(k)+c1+c2i2)%m,i=0,1,…,m−1
双重散列(double hashing): 重新计算 hash 结果
h(k,i)=(h1(k)+ih2(k))%m

哈希函数

选取的哈希函数应当使每个key都被尽可能地分配到每个槽,并且使得余下的key不发生冲突。

装载因子

重哈希

简易哈希表ADT实现

三个最常用操作:

add(key, value)
get(key, default)
remove(key)
class Slot(object):
    """定义一个 hash 表 数组的槽
    注意,一个槽有三种状态,看你能否想明白
    1.从未使用 HashMap.UNUSED。此槽没有被使用和冲突过,查找时只要找到 UNUSED 就不用再继续探查了
    2.使用过但是 remove 了,此时是 HashMap.EMPTY,该探查点后边的元素扔可能是有key
    3.槽正在使用 Slot 节点
    """
    def __init__(self, key, value):
        self.key, self.value = key, value

class HashTable(object):
    pass

实现中,slot有三个状态:没有被使用过unused、使用过但是被删除empty、正在被使用。
是因为,要牵涉到删除、增加的操作。

你可能感兴趣的:(Python,Python数据结构与算法)