Python 算法之 设计哈希集合

Python 算法之 设计哈希集合


哈希集合🎏

到底 什么是哈希集合🤔

  • 哈希集合 『hash set』,是一种通过哈希值来定位存储位置的数据结构,只存储Key本身,没有对应值,通过哈希把 key 转成哈希值作为数据存储的位置索引,而该位置上存储内容为key本身
  • 哈希集合 不能存储重复元素 key,因为名称就已经说的很清楚了,它是个集合

给出题目🎎

不使用任何内建的哈希表库设计一个哈希集合(HashSet)

实现 MyHashSet 类:

  • add(key) 方法: 向哈希集合中插入值 key
  • remove(key) 方法: 将给定值 key 从哈希集合中删除,如果哈希集合中没有这个值,则什么都不做
  • contains(key) -> bool: 返回哈希集合中是否存在这个值 key

输入:
[“MyHashSet”, “add”, “add”, “contains”, “contains”, “add”, “contains”, “remove”, “contains”]
[[], [1], [2], [1], [3], [2], [2], [2], [2]]
输出:
[null, null, null, true, false, null, true, null, false]

思路:😆

为对于 哈希集合来说(hashset),只存储Key本身,没有对应值,那只用判断这个key 是否存在即可,如果存在的返回True,反之返回False。
根据这个思路,有以下几种解题方法


方法一:简单大数组

将数组元素设计为 布尔型 bool,key 为数组的索引,取值为Ture代表key存在,为Fasle时则代表数据不存在

特点

  • 编写简单,容易理解
  • 查找和删除的速度快,根据索引只用访问一次
  • 最大的一个缺陷,就是需要一个很大的数组去存储元素,占用了非常大的空间,当数据范围很大的时候,这个方法会出现问题,当数据范围较小的时候,又会因为占用了很大的空间导致性价比极低
  • 需要知道数据的范围大小
class MyHashSet:
    
    def __init__(self):
        self.hashset = [False] * 1000000      # 新建了长度为 1000000 的数组
        
    def add(self, key: int):
        """
        添加值
        :rtype: None 
        """
        self.hashset[key] = True
        
    def remove(self, key: int):
        """
        删除值
        :rtype: None 
        """
        self.hashset[key] = False
        
    def contains(self, key) -> bool:
        """
        是否包含
        :return: 布尔类型
        """
        return self.hashset[key]

执行效率:

  • 时间复杂度: O(1)
  • 空间复杂度: O(自定数据范围)

方法二:链地址法

通过将 key 求出哈希值 后可以取得存储位置,但不同的 key 求出的哈希值可能会出现一样的情况,这就照成了冲突,所以不能直接存储元素,而是将每个位置指向一个链表用于存储元素。

不定长拉链数组

特点

  • 节约内存空间
  • 通过哈希值获取数据存储位置后,需要在链表中遍历查找元素
  • 不需要知道数据的范围大小
class MyHashSet:

    def __init__(self):
        self.BASE = 967             # 将其设置为质数,尽量减少冲突
        self.hashset = [[] for _ in range(self.BASE)]

    def hash(self, key):
        return key % self.BASE      # 使用求余来代替hash函数

    def add(self, key: int):
        """
        添加值
        :rtype: None
        """
        hashkey = self.hash(key)
        if key not in self.hashset[hashkey]:
            self.hashset[hashkey].append(key)

    def remove(self, key: int):
        """
        删除值
        :rtype: None
        """
        hashkey = self.hash(key)
        if key in self.hashset[hashkey]:
            self.hashset[hashkey].remove(key)

    def contains(self, key) -> bool:
        """
        是否包含
        :return: 布尔类型
        """
        hashkey = self.hash(key)
        return key in self.hashset[hashkey]

执行效率:

  • 时间复杂度: O(1)
  • 空间复杂度: O(自定数据范围)

定长拉链数组

特点

  • 需要知道数据的范围大小
class MyHashSet:

    def __init__(self):
        self.BASE = 1669                                     # 将其设置为质数,尽量减少冲突
        self.hashset = [False for _ in range(self.BASE)]

    def hash(self, key):        
        return key % self.BASE                               # 使用求余来代替hash函数                     

    def index(self, key):                                    # 获取第二维度位置
        return key // self.BASE
    
    def add(self, key: int):
        """
        添加值
        :rtype: None
        """
        hashkey, index = self.hash(key), self.index(key)
        if not self.hashset[hashkey]:                        # 只有需要时才会构建第二维度数组
            self.hashset[hashkey] = ([False]*(self.BASE+1))  # +1 是因为 679//679 时不为0
        self.hashset[hashkey][index] = True

    def remove(self, key: int):
        """
        删除值
        :rtype: None
        """
        hashkey, index = self.hash(key), self.index(key)
        if self.hashset[hashkey]:
            self.hashset[hashkey][index] = False
            
    def contains(self, key) -> bool:
        """
        是否包含
        :return: 布尔类型
        """
        hashkey, index = self.hash(key), self.index(key)
        return self.hashset[hashkey] and self.hashset[hashkey][index]

方法三:散列表

哈希表『hash table』,又叫散列表,根据输入关键字 key 来进行访问的数据结构,通过哈希把 key 转换成哈希值作为数据存储的位置索引,从而确定 值 (value) 存储在什么地方

散列表

  • 散列表总是会将同样的输入 (key) 映射到相同的索引得到相同的值 (value),不同的输入会输出不同的值
  • 散列表的查找、插入和删除速度都特别快
  • Python 提供的 字典 即为散列表的实现

需要注意的是,题目要求我们 不使用任何内建的哈希表库,那么这种方法是违规的,这里作为参考

class MyHashSet:

    def __init__(self):
        self.hashset = {}

    def add(self, key: int):
        """
        添加值
        :rtype: None
        """
        self.hashset[key] = True

    def remove(self, key: int):
        """
        删除值
        :rtype: None
        """
        self.hashset[key] = False

    def contains(self, key) -> bool:
        """
        是否包含
        :return: 布尔类型
        """
        return self.hashset[key] if key in self.hashset else False

执行效率:

  • 时间复杂度: O(1)
  • 空间复杂度: O(n)

使用集合

利用集合的去重性

class MyHashSet:

    def __init__(self):
        self.hashset = set()        # 新建一个集合

    def add(self, key: int):
        """
        添加值
        :rtype: None
        """
        self.hashset.add(key)       # 集合天生去重

    def remove(self, key: int):
        """
        删除值
        :rtype: None
        """
        if key in self.hashset:
            self.hashset.remove(key)

    def contains(self, key) -> bool:
        """
        是否包含
        :return: 布尔类型
        """
        return True if key in self.hashset else False

参考资料

  • 题目出之力扣:设计哈希集合 leetcode 705
  • 感谢题解里 负雪明烛 大佬所带给我的启发

由衷感谢💖


相关博客😏

  • 5
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
### 回答1: 我可以给您提供一个Python示例代码以实现局部敏感哈希算法:def lsh(s, b, r): # s 为输入字符串,b 为哈希分块大小,r 为哈希函数的数量 n = len(s) # 初始化一个列表来存储哈希值 hashes = [] # 遍历所有 r 个哈希函数 for i in range(r): h = 0 # 遍历字符串中的每个字符 for j in range(0, n, b): # 如果当前字符不足 b 个,仅使用实际字符数目 m = min(b, n-j) # 计算当前字符块的哈希值 h = h + hash(s[j:j+m]) # 将 h 添加到哈希列表中 hashes.append(h) # 返回哈希列表 return hashes ### 回答2: 局部敏感哈希算法(Locality Sensitive Hashing,简称LSH)是一种常用于解决数据检索问题的算法。它的目标是在高维数据中快速找到与给定查询相似的数据项。 要用Python编写局部敏感哈希算法,可以按照以下步骤进行: 1. 首先,选择一个合适的哈希函数。在局部敏感哈希算法中,常用的哈希函数包括随机投影、MinHash和SimHash等。 2. 准备需要检索的数据集。假设我们有一个包含大量文档的文本集合。 3. 对每个文档,使用选择的哈希函数将文档哈希为一个固定长度的二进制编码。可以使用Python哈希函数库来实现如下: ```python import hashlib def hash_document(document, hash_size): hash_value = hashlib.md5(document.encode('utf-8')).hexdigest() binary_hash = bin(int(hash_value, base=16))[2:].zfill(hash_size) return binary_hash ``` 其中,`document`表示待哈希的文档,`hash_size`表示哈希编码的长度。这里使用MD5哈希函数将文档编码为哈希值,并将其转换为二进制编码。 4. 组织哈希索引。将每个文档的哈希编码存储到一个哈希索引中,可以使用Python的字典数据结构来实现。键是哈希编码的值,值是与该哈希编码相对应的文档标识符列表。 ```python def build_hash_index(documents, hash_size): hash_index = {} for doc_id, doc_content in enumerate(documents): binary_hash = hash_document(doc_content, hash_size) if binary_hash in hash_index: hash_index[binary_hash].append(doc_id) else: hash_index[binary_hash] = [doc_id] return hash_index ``` 其中,`documents`是由文档组成的列表。 5. 执行查询。对于给定的查询文档,使用相同的哈希函数将其编码为哈希值,并在哈希索引中查找相似的哈希编码。 ```python def query_similar_documents(query, hash_index, hash_size, threshold): query_hash = hash_document(query, hash_size) similar_documents = set() for index_hash, doc_ids in hash_index.items(): if hamming_distance(query_hash, index_hash) <= threshold: similar_documents.update(doc_ids) return similar_documents ``` 其中,`query`表示查询文档,`threshold`是哈希距离的阈值。 通过以上步骤,我们可以用Python编写一个简单的局部敏感哈希算法。当然,在实际应用中,还可以根据具体需求对算法进行优化和改进。 ### 回答3: 局部敏感哈希(Locality Sensitive Hashing,LSH)是一种常用于数据相似性搜索的算法。下面是使用Python编写局部敏感哈希算法的例子: 首先,我们需要安装第三方库`numpy`和`sklearn`,使用以下命令进行安装: ``` pip install numpy pip install sklearn ``` 接下来,我们可以开始编写局部敏感哈希算法的代码。下面是一个简单的示例: ```python import numpy as np from sklearn.neighbors import LSHForest # 创建一个随机数据集 data = np.random.random((100, 10)) # 初始化局部敏感哈希算法对象 lshf = LSHForest(n_estimators=10, random_state=42) # 训练数据集 lshf.fit(data) # 通过查询相似项 query_data = np.random.random((1, 10)) nearest_neighbors = lshf.kneighbors(query_data, n_neighbors=5, return_distance=False) # 打印最近的邻居 print(nearest_neighbors) ``` 在上面的代码中,我们首先创建了一个随机的数据集`data`,然后初始化了一个局部敏感哈希算法对象`lshf`。我们使用`fit`方法对数据集进行训练,然后使用`kneighbors`方法查询与给定数据`query_data`相似的最近邻居,最后打印出最近邻居的索引。 通过这个例子,我们可以看到,使用`sklearn`库的`LSHForest`类可以很方便地实现局部敏感哈希算法。这个算法在大规模数据相似性搜索中有广泛的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值