Datawhale11月组队学习leetcode

Task01 哈希表(1-2天)

1.1哈希表简介

哈希表(Hash Table):也叫做散列表。是根据关键码值(Key Value)直接进行访问的数据结构。

哈希表通过「键 key 」和「映射函数 Hash(key) 」计算出对应的「值 value」,把关键码值映射到表中一个位置来访问记录,以加快查找的速度。这个映射函数叫做「哈希函数(散列函数)」,存放记录的数组叫做「哈希表(散列表)」。

1.2哈希函数

哈希函数(Hash Function):将哈希表中元素的关键键值映射为元素存储位置的函数。

关于整数类型的关键字,通常用到的哈希函数方法有:直接定址法、除留余数法、平方取中法、基数转换法、数字分析法、折叠法、随机数法、乘积法、点积法等。

  • 直接定址法:
    取关键字本身 / 关键字的某个线性函数值 作为哈希地址。即:Hash(key) = key 或者 Hash(key) = a * key + b,其中 a 和 b 为常数。(适合于关键字分布基本连续的情况)

  • 除留余数法:
    假设哈希表的表长为 m,取一个不大于 m 但接近或等于 m 的质数 p,利用取模运算,将关键字转换为哈希地址。即:Hash(key) = key % p,其中 p 为不大于 m 的质数。(一般 p 取素数或者 m)

  • 平方取中法:
    先通过求关键字平方值的方式扩大相近数之间的差别,然后根据表长度取关键字平方值的中间几位数为哈希地址。

  • 基数转换法:
    将关键字看成另一种进制的数再转换成原来进制的数,然后选其中几位作为哈希地址。

1.3 哈希冲突

哈希冲突(Hash Collision):不同的关键字通过同一个哈希函数可能得到同一哈希地址,即 key1 ≠ key2,而 Hash(key1) = Hash(key2),这种现象称为哈希冲突。

常用的哈希冲突解决方法主要是两类:「开放地址法(Open Addressing)」 和 「链地址法(Chaining)」。

  • 开放地址法:

将哈希表中的「空地址」向处理冲突开放。当哈希表未满时,处理冲突时需要尝试另外的单元,直到找到空的单元为止。

当发生冲突时,开放地址法按照下面的方法求得后继哈希地址:H(i) = (Hash(key) + F(i)) % m,i = 1, 2, 3, …, n (n ≤ m - 1)。

H(i) 是在处理冲突中得到的地址序列;Hash(key) 是哈希函数;m 是哈希表表长;F(i) 是冲突解决方法,取法可以有:线性探测法、二次探测法、伪随机数序列。

  • 链地址法:

将具有相同哈希地址的元素(或记录)存储在同一个线性链表中。

字典

在python中,字典就是一个哈希表实现。

https://www.runoob.com/python3/python3-dictionary.html

2 练习(day1)

2.1存在重复元素

描述:给定一个整数数组 nums。

要求:判断是否存在重复元素。如果有元素在数组中出现至少两次,返回 True;否则返回 False。

思路一:使用set() 函数创建一个无序不重复元素集,(可进行关系测试,删除重复数据,还可以计算交集、差集、并集等),若有重复元素,则去重后,列表长度必然小于原列表长度,故判断二者长度是否一致即可。https://www.runoob.com/python3/python3-set.html

思路二:哈希表。将数组中每个元素插入到哈希表中。如果插入一个元素时发现该元素已经存在于哈希表中,则说明存在重复的元素。python中利用字典实现(key是唯一的,但value则不必)。

from typing import List
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        return not len(set(nums)) == len(nums)
class Solution:
    def containsDuplicate(self, nums: List[int]) -> bool:
        numdict = {}    #建一个空字典
        # 哈希表 key 为 nums1 数组中的数,value 为值
        for i in nums:    
            if i not in numdict:    #判断nums中的数是否在字典中
                numdict[i] = 1       #不在,则将该键添加进字典并赋值1(随便赋值)
            else:
                return True         #在,则有重复,返回T,跳出循环
        return False                #无重复,返回F

2.2存在重复元素 II

描述:给你一个整数数组 nums 和一个整数 k ,判断数组中是否存在两个不同的索引 i 和 j ,满足 nums[i] == nums[j] 且 abs(i - j) <= k 。如果存在,返回 true ;否则,返回 false 。

思路:遍历数组并记录下标,将数组元素作为key插入字典,下标为value,key重复时计算此时下标差值,<=k则输出T,否则修改对应value为最大下标,继续遍历。

enumerate(seq,start)函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,返回枚举对象(index,seq)

class Solution:
    def containsNearbyDuplicate(self, nums: List[int], k: int) -> bool:
        numdict = {}          
        for i,num in enumerate(nums):    #遍历序列的同时,获得索引i
            if num in numdict and (i-numdict[num]) <= k:    
                return True       #num在字典中,且两数距离小于k,返回T
            numdict[num] = i    #不在-添加进字典,在-修改value,value为当前下标
        return False     #不满足条件,返回F

2.3有效的数独

描述:请你判断一个 9 x 9 的数独是否有效。只需要根据以下规则 ,验证已经填入的数字是否有效即可。

数字 1-9 在每一行只能出现一次。
数字 1-9 在每一列只能出现一次。
数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。
一个有效的数独(部分已被填充)不一定是可解的。
只需要根据以上规则,验证已经填入的数字是否有效即可。
空白格用 ‘.’ 表示。

思路:定义三个二维数组,循环遍历九宫格中每个元素,判断是否在每一行每一列每一小格中,在则无效,不在则更新数组

class Solution:
    def isValidSudoku(self, board: List[List[str]]) -> bool:
        row = [set() for _ in range(9)]  #创建行空数组
        col = [set() for _ in range(9)]  #创建列空数组
        block = [set() for _ in range(9)]  #创建格子空数组
        for i in range(9):
            for j in range(9):    #遍历九宫格内每个元素
                if board[i][j] != '.':  #非空白格
                    num = board[i][j]
                    if num in row[i] or num in col[j] or num in block[(i//3)*3+(j//3)]:
                        return False  #判断该数在行、列、格子中是否存在,存在则数独无效,返回F
                    row[i].add(num)     #不存在则添加进对应数组
                    col[j].add(num)
                    block[(i//3)*3+(j//3)].add(num)
        return True      #遍历完毕,数独有效,返回T

3练习(day2)

3.1两个数组的交集

描述:给定两个数组 nums1 和 nums2 ,返回它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。
思路一:将列表转换成集合,求交集,最后转换回列表输出
思路二:创建一个哈希表用于判断是否有重复,一个列表存储交集

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        return list(set(nums1)&set(nums2))
class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:

        # 有一个数组为空,则交集为空
        if not nums1 or not nums2:
            return []

        hash = {}      # 哈希表
        res = []       # 结果列表,存放最后结果

        # 哈希表 key 为 nums1 数组中的数,value 为值,赋1
        for i in nums1:
            if i not in hash:
                hash[i] = 1
        # 遍历 nums2,如果 nums2 数组中的数出现在哈希表中,对应数放入结果列表,对应 value 值置为0
        for j in nums2:
            if j in hash and hash[j]==1:
                res.append(j)
                hash[j] = 0
        return res

3.2两个数组的交集 II

描述:给你两个整数数组 nums1 和 nums2 ,请你以数组形式返回两数组的交集。返回结果中每个元素出现的次数,应与元素在两个数组中都出现的次数一致(如果出现次数不一致,则考虑取较小值)。可以不考虑输出结果的顺序。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:

        # 有一个数组为空,则交集为空
        if not nums1 or not nums2:
            return []

        hash = {}      # 哈希表
        res = []       # 结果列表,存放最后结果

        # 哈希表 key 为 nums1 数组中的数,value 为值,赋1
        for i in nums1:
            if i not in hash:
                hash[i] = 1
            else:
                hash[i]+=1
        # 遍历 nums2,如果 nums2 数组中的数出现在哈希表中,对应数放入结果列表,对应 value 值置为0
        for j in nums2:
            if j in hash and hash[j]>0:
                res.append(j)
                hash[j]-=1
        return res

counter() 函数:Python标准库 collections 里的 counter() 函数是一个计数器工具,用于统计可迭代对象中元素出现的次数,并返回一个字典(key-value)key 表示元素,value 表示各元素 key 出现的次数,可为任意整数 (即包括0与负数)。

class Solution:
    def intersect(self, nums1: List[int], nums2: List[int]) -> List[int]:
        if len(nums2) < len(nums1):
            return self.intersect(nums2, nums1)

        m = collections.Counter(nums1)
        res = []
        for num in nums2:
            if num in m:
                res.append(num)
                m[num] -= 1
                if m[num] == 0:
                    m.pop(num)

        return res

3.3设计哈希映射

描述:不使用任何内建的哈希表库设计一个哈希映射(HashMap)。

实现 MyHashMap 类:

MyHashMap() 用空映射初始化对象
void put(int key, int value) 向 HashMap 插入一个键值对 (key, value) 。如果 key 已经存在于映射中,则更新其对应的值 value 。
int get(int key) 返回特定的 key 所映射的 value ;如果映射中不包含 key 的映射,返回 -1 。
void remove(key) 如果映射中存在 key 的映射,则移除 key 和它所对应的 value 。

思路一:超大数组
思路二:拉链法
(1)对于put(key, value)操作:
我们先求出key的哈希值(取模),然后遍历该位置上的链表:
如果链表中包含key,则更新其对应的value;
如果链表中不包含key,则直接将(key,value)插入该链表中。
(2)对于get(key)操作:
求出key对应的哈希值后,遍历该位置上的链表.
如果key在链表中,则返回其对应的value,否则返回-1。
(3)对于remove(key),求出key的哈希值后,遍历该位置上的链表,如果key在链表中,则将其删除。

class MyHashMap:

    def __init__(self):
        self.map = [-1]*1000001

    def put(self, key: int, value: int) -> None:
        self.map[key] = value

    def get(self, key: int) -> int:
        return self.map[key]

    def remove(self, key: int) -> None:
        self.map[key] = -1



# Your MyHashMap object will be instantiated and called as such:
# obj = MyHashMap()
# obj.put(key,value)
# param_2 = obj.get(key)
# obj.remove(key)
class MyHashMap:

    def __init__(self):
        self.buckets = 1009       # 开辟一个大数组,长度为质数
        self.table = [[] for _ in range(self.buckets)]

    def hash(self, key):         #求key的哈希值(取模)
        return key % self.buckets
    
    def put(self, key: int, value: int) -> None:
        hashkey = self.hash(key)
        for item in self.table[hashkey]:      #遍历哈希到的链表中,查找key,并更新值
            if item[0] == key:
                item[1] = value
                return       # 更新完之后,直接返回
        self.table[hashkey].append([key, value])   # 如果链表中找不到对应的key,将其新添到链表中

    def get(self, key: int) -> int:
        hashkey = self.hash(key)
        for item in self.table[hashkey]:
            if item[0] == key:
                return item[1]
        return -1           # 可能哈希的位置,所对应的链表不为空,但是不存在该值

    def remove(self, key: int) -> None:
        hashkey = self.hash(key)
        for i, item in enumerate(self.table[hashkey]):
            if item[0] == key:
                self.table[hashkey].pop(i)
                return

作者:负雪明烛
链接:https://leetcode.cn/problems/design-hashmap/solutions/654877/xiang-jie-hashmap-de-she-ji-zai-shi-jian-85k9/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值