社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

菜鸟学Python之哈希表

小明同志 • 5 年前 • 228 次点击  
阅读 48

菜鸟学Python之哈希表

哈希表

在学习Python的时候有时候会想,为什么dict和set的查找速度这么快,感觉就像是事先知道要找的元素的位置一样?在学完哈希表之后,这个问题也就够被很好的解释了。

定义

哈希表是一种根据关键码(key)去寻找(value)的数据映射结构,该结构通过把关键码映射的位置(index)去寻找存放值的地方。 举个例子,也就是像小时候我们经常查的字典一样,比如我们要查找一个字 “一”(value),我们先得到它的拼音“yi”(key),然后就可以在字典的查找目录看到这个字在哪一页(index),最后就得到这个字的详细信息。

实现方法

我们知道,数组的查找速度之所以是O(1)是因为数组里面的元素都有一个下标,所以参考数组的下标,给每个元素一种[逻辑下标]。我们把得到的逻辑下标称为“”。

逻辑下标的计算方法采用的是取模运算: h(key) = key % M

哈希冲突

当给出的值取到的“逻辑下标”相同时,哈希冲突便产生了。这里引用一下别人的图

解决办法

遇到这种情况该如何解决呢?我们首先能够想到的是既然冲突了,那能不能够把这些冲突的放进一个链表里面呢?或者重新找过其他地方呢?

  1. 让数组中 冲突的槽 变成一个链式结构,但是这个方法有个缺点,当链表太长的时候,就会造成时间退化,查找时间会从O(1)退化为O(n),因此这种方法比较少用。
  2. 寻找下一个槽,这里给出的是比较常用的二次探查(以二次方作为偏移量)

装载因子(load factor)

load factor = 元素个数 / 哈希表大小, 当装载因子超过0.8时,就要开辟新的空间并重新进行散列了。

重哈希(Rehashing)

重新开辟空间并散列的操作过程就叫做重哈希

代码示例

下面给出实现哈希表的代码

# 哈希表是用数组完成的
class Array(object):
    def __init__(self, size=32, init=None):
        self._size = size
        self._items = [init] * 32    # 得到一个空的数组
        
    # 返回value
    def __getiter__(self, index):    
        return self._items[index]
    
    # 重置value
    def __setitem__(self, index, value):
        self._items[index] = value

    def __len__(self):
        return self._size

    def clear(self, value=None):
        for i in range(len(self._items)):
            self._items[i] = value

    def __iter__(self):
        for item in self._items:
            yield item


# 定义槽,传入key和value
class Slot(object):
    def __init__(self, key, value):
        self.key = key
        self.value = value


# 定义哈希表
class HashTable(object):
    # 首先定义两个全局变量
    UNUSED = None
    EMPTY = Slot(None, None)

    def __init__(self):
        self._table = Array(8, init=HashTable.UNUSED)
        self.length = 0  # 已使用槽的数量
    
    def __load_factor(self):
        """定义装载因子"""
        return self.length / float(len(self._table))
    
    def __len__(self):
        return self.length
    
    def __hash__(self):
        """定义哈希函数"""
        return abs(hash(key)) % len(self._table)
    
    
    def _find_key(self, key):
        """根据给出的key,获得index"""
        index = self.__hash__(key)
        _len = len(self._table)
        while self._table[index] is not HashTable.UNUSED:   # 这个槽已经被使用过且为空,则重新查找
            if self._table[index] is HashTable.EMPTY:
                index = (index * 5 + 1) % _len
                continue
            elif self._table[index].key == key:
                return index
            else:
                index = (index * 5 + 1) % _len
        return None

    def _slot_can_insert(self, index):
        """判断找到的槽是否可用"""
        return (self._table[index] is HashTable.UNUSED or self._table[index] is HashTable.EMPTY)

    def _find_slot_for_insert(self, key):
        """查找可以用的槽"""
        index = self.__hash__(key)
        _len = len(self._table)
        while not self._slot_can_insert(index):
            index = (index * 5 + 1) % _len
        return


    
 index

    def __contains__(self, key):
        index = self._find_key(key)
        return index is not None

    def add(self, value, key):
        """往哈希表里添加数据"""
        if key in self:
            index = self._find_key(key)
            self._table[index] = value
            return False
        else:
            index = self._find_slot_for_insert(key)
            self._table[index] = Slot(key, value)
            self.length += 1
            if self.__load_factor() > 0.8:
                self.rehash()
            return True

    def _rehash(self):
        """重哈希"""
        old_table = self._table
        newsize = len(self._table) * 2
        self._table = Array(newsize, HashTable.EMPTY)
        self.length = 0

        for slot in old_table:
            if slot is not HashTable.UNUSED or slot is not HashTable.EMPTY:
                index = self._find_slot_for_insert(slot.key)
                self._table[index] = slot
                self.length += 1

    def get(self, key, default=None):
        """取值"""
        index = self._find_key(key)
        if index is None:
            return default
        else:
            return self._table[index].value

    def remove(self, key):
        index = self._find_key(key)
        if index is None:
            return KeyError()
        value = self._table[index]
        self.length -= 1
        self._table[index] = HashTable.EMPTY
        return value

    def __iter__(self):
        for slot in self._table:
            if slot not in (HashTable.UNUSED, HashTable.EMPTY):
                yield slot
复制代码

哈希表是很高效的数据结构,对于新手来讲也比较难理解,我手写了一遍代码,然后再用电脑敲了一遍才基本了解。新手写的,请见谅

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/31382
 
228 次点击