python哈西表

Posted by Dugreen on December 20, 2017

最近帮一个国际学院的同学讲解哈希表与哈希函数的内容,发现当初学java的时候有关哈希表的内容学的太过浅薄,今天拿出晚上的时间来彻底加深学习一下哈西表与哈希函数。 我们在学习数据结构的内容时,会发现对线性表进行线性查找的时间复杂度是O(n),尽管使用二分查找这种改进的查找方式,所进行的操作的时间复杂度仍为O(log n),但是使用哈希表可以则可以进行非常快速的查找操作,查找时间复杂度为常数,同时不需要元素排列有序,很吃惊吧!python的内建数据类型:字典,就是用哈希表实现的。

1.先来讲一下什么是哈希:

hash function – maps a key to an array index
搜索的key经过一种算法的转换,唯一的映射成一个index。
通常计算机课本上都是通过mod (%)来实现的哈希操作。
h(key) = key % M

2.什么是哈希表:

hash table – the list containing the keys
包含这些key的集合

3.关于冲突(Collisions)

当我们对元素进行哈希函数的操作后得到的唯一映射(index)相同,当我们存储这些元素时就产生了冲突。我们如何解决这种存储的冲突是哈西表实现的重点。
线性探测解决碰撞(Resolving Collisions with Linear Probing)

4.查找:

当我们进行查找时,首先假设被查找元素为 i ,首先对i 进行哈希函数的操作然后得到数组的索引。根据索引查找值.

#!coding:utf-8

"""
哈希表的实现
以下代码摘取自:http://www.cnblogs.com/hanahimi/p/4765265.html
"""

class LinearMap(object):
    """ 线性表结构 """
    def __init__(self):
        self.items = []

    def add(self, k, v):    # 往表中添加元素
        self.items.append((k,v))

    def get(self, k):       # 线性方式查找元素
        for key, val in self.items:
            if key==k:      # 键存在,返回值,否则抛出异常
                return val
        raise KeyError

class BetterMap(object):
    """ 利用LinearMap对象作为子表,建立更快的查询表 """
    def __init__(self,n=100):
        self.maps = []          # 总表格
        for i in range(n):      # 根据n的大小建立n个空的子表
            self.maps.append(LinearMap())

    def find_map(self,k):       # 通过hash函数计算索引值
        index = hash(k) % len(self.maps)
        return self.maps[index] # 返回索引子表的引用

    # 寻找合适的子表(linearMap对象),进行添加和查找
    def add(self, k, v):
        m = self.find_map(k)
        m.add(k,v)

    def get(self, k):
        m = self.find_map(k)
        return m.get(k)

class HashMap(object):
    def __init__(self):
        # 初始化总表为,容量为2的表格(含两个子表)
        self.maps = BetterMap(2)
        self.num = 0        # 表中数据个数

    def get(self,k):
        return self.maps.get(k)

    def add(self, k, v):
        # 若当前元素数量达到临界值(子表总数)时,进行重排操作
        # 对总表进行扩张,增加子表的个数为当前元素个数的两倍!
        if self.num == len(self.maps.maps):
            self.resize()

        # 往重排过后的 self.map 添加新的元素
        self.maps.add(k, v)
        self.num += 1

    def resize(self):
        """ 重排操作,添加新表, 注意重排需要线性的时间 """
        # 先建立一个新的表,子表数 = 2 * 元素个数
        new_maps = BetterMap(self.num * 2)

        for m in self.maps.maps:  # 检索每个旧的子表
            for k,v in m.items:   # 将子表的元素复制到新子表
                new_maps.add(k, v)

        self.maps = new_maps      # 令当前的表为新表

if __name__=="__main__":
    table = BetterMap()
    pricedata = [("Hohner257",257),
                 ("SW1664",280),
                 ("SCX64",1090),
                 ("SCX48",830),
                 ("Super64",2238),
                 ("CX12",1130),
                 ("Hohner270",620),
                 ("F64C",9720),
                 ("S48",1988)]

    for item, price in pricedata:
        table.add(k=item, v=price)

    print table.get("CX12")
    # >>> 1130
    print table.get("QIMEI1248")
    # >>> raise KeyError