Leetcode哈希表

目录

哈希表基础知识

  1. 哈希表/散列表 (hashmap):是根据关键码的值而直接进行访问的数据结构。
    数组就是一张哈希表,数组索引即为关键码
  2. 应用范围:一般哈希表都是用来快速判断一个元素是否出现集合里查找的时间复杂度为O(1)
  3. python中的常见哈希结构:
    数组[]、集合hashset = set()——相当于仅有key的字典、字典hashmap = {}
    各自应用范围:
    数组:当数据量小于1000,且数据之间较连续 如:1 2 4 5 8;注意:若数值间间隔很大则不适合使用数组,会浪费很多内存,如:1 2 1000 存三个量却占用1000个存储单位(由于数组存储空间连续)。通过下标查找元素时间复杂度O(1),若是查找一个元素是否在数组中出现,时间复杂度O(n)


    集合:数据量不宜过大,数据不重复,无序,查找某个元素方式为哈希,时间复杂度O(1)
    设置一个空集合: set()
    设置有数据的集合:{‘haha’,‘heihei’,‘neinei’}
    注:add() 为集合添加元素; pop() 随机移除元素


    字典:同时需要存储key和val
    设置一个空字典:dict{}
    设置有数据的字典:{‘haha’: 1,‘heihei’: 2,‘neinei’: 3}

  4. 若使用字符作为关键值,需要使用哈希函数 (hash function)
    在这里插入图片描述
    hash code 是使用特定编码方式,将其他数据格式转化为不同的数值,这样就把学生名字映射为哈希表上的索引数字了。
    此时为了保证映射出来的索引数值都落在哈希表上,我们会在再次对数值做一个取模的操作,就要我们就保证了学生姓名一定可以映射到哈希表上了。
    如果学生的数量大于哈希表的大小,此时会出现有几位学生的名字同时映射到哈希表同一个索引下标的位置——哈希碰撞。一般哈希碰撞有两种解决方法, 拉链法和线性探测法。

242 有效的字母异位词

题目

在这里插入图片描述

分析

若s和t中字符同且字符数也相同,则s和t是字母异位词。统计s中出现的字符并统计出现的次数,以字符做key,字符数做val,构建哈希表。遍历t中的字符,出现一次则对哈希表中的值减一。若是有效的字母异位,最终的hashmap应该全0
由于字符全为小写字符,字符数26,使用ASCII的相对值,可使用数组构造哈希表。

代码

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False
            
        hashmap  = [0]*26
        for i in s:
            hashmap[ord(i)-ord('a')] += 1
        for i in t:
            hashmap[ord(i)-ord('a')] -= 1
        for i in hashmap:
            if i != 0:
                return False
        return True

注:ord() :返回对应字符的ascii码


进阶——不可直接使用数组了,可用字典构建哈希表

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s)!=len(t):
            return False
        hashmap = {}
        for i in s:
            if i in hashmap:
                hashmap[i] += 1  
                # 不用ord()则索引是字符 内部使用了hash函数 即通过hashcode将其他字符转为数字
            else:
                hashmap[i] = 1
        for i in t:
            if i in hashmap:
                hashmap[i] -= 1
                if hashmap[i] < 0:  # 字符一样 数量有多有少,则一定有负
                    return False
            else:
                return False  # 出现s中没有的字符 直接False
        return True


349 两个数组的交集

题目

在这里插入图片描述

分析

  1. 构建字典型的哈希表,nums1中出现的数作为key,val设为1;
  2. 遍历nums2中的元素
    ① 若该元素在哈希表中且val不等于0(说明该数第一次出现),则将该元素加到返回表中,val设为0;
    ② 若该元素在哈希表但val等于0(说明该数已经出现过了),无需操作;
    ③ 若该元素不在哈希表中,也无需操作。最后返回返回表即可

通过对val的更改实现结果的去重

代码

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res =[]
        hashmap = {}
        for i in nums1:
            if i not in hashmap:  # hashmap中保存nums1中出现的所有元素 val=1
                hashmap[i] = 1
        for i in nums2:
            if i in hashmap and hashmap[i] == 1:
                res.append(i)  # nums1和nums2中相同的元素加到res中 赋为0是为了结果中元素唯一
                hashmap[i] = 0
        return res


202 快乐数

题目

在这里插入图片描述

分析

由于题目给出不快乐数会进入循环,即在替换过程中,会有数字重复出现。我们可以将已出现的数字保存在hashset中,若某个数字重复出现,则立即返回False,否则当出现’1’时,即可认为该数为快乐数,返回True

代码

class Solution:
    def isHappy(self, n: int) -> bool:
        hashset = set()
        while n not in hashset:
            if n == 1:
                return True
            hashset.add(n)  # set:   .add()   .remove()
            
            sqrsum = 0
            while n != 0:  # 按位取平方 求和
                sqrsum = sqrsum + (n%10)**2
                n = n//10
            n = sqrsum
        return False

注意:对整数按位取平方求和的方法 除以10取余


集合:

  1. 创建
    大括号 { } / set() 函数
    注意:创建一个空集合必须用 set() 而不是 { },因为 { } 是用来创建一个空字典
  2. 运算
    交集 & : x&y,返回一个新的集合,包括同时在集合 x 和y中的共同元素。
    并集 | : x|y,返回一个新的集合,包括集合 x 和 y 中所有元素。
    差集 - : x-y,返回一个新的集合,包括在集合 x 中但不在集合 y 中的元素。
    补集:通过差集可实现补集
  3. 操作
    添加元素
    .add() :添加的元素,只能是数字、字符串、元组或者布尔类型(True 和 False)值(不可变),不能添加列表、字典、集合这类可变的数据。如果元素已存在,则不进行任何操作(集合的互异性)。
    .update():传入的参数必须是可迭代对象(列表,元组,字典等)(可用于添加一个元素、或者多个元素)。若set.updata(‘python’),则会被按元素拆分;若set.updata([python]),则输入参数被看作集合,添加的是python。由于’python’使字符串,不可修改
    删除元素
    .remove() :如果被删除元素本就不包含在集合中,则此方法会抛出 KeyError 错误
    .discard() :和remove同,唯一区别在于,当删除集合中元素失败时,此方法不会抛出任何错误。
    .pop():随机删除
    清空集合:.clear()
    计算集合元素个数:len()
a = {"Hello", "HaiCoder", 1024}
a.update("Python")        
print("Set =", a)

b = {"Hello", "HaiCoder", 1024}
b.update(['Python', 'matlab'])  # 放进集合
print("Set =", b)
结果:
Set = {1024, 'y', 't', 'n', 'Hello', 'P', 'HaiCoder', 'o', 'h'}
Set = {1024, 'matlab', 'Hello', 'Python', 'HaiCoder'}


1 两数之和

题目

在这里插入图片描述

代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        n = len(nums)
        hashmap = {}
        for i in range(n):  # i是下标
            diff = target - nums[i]
            if diff in hashmap:
                return [hashmap[diff],i]
            else:
                hashmap[nums[i]] = i  # 数值作为键,下标作为值          

注:要求返回下标,不可使用双指针



454 四数相加Ⅱ

题目

在这里插入图片描述

分析

只要求输出满足的个数 不要求下标
# 一种方法是四层循环 时O(n^4)
# 另一种是先nums1和num2组合,求出所有可能的和;在nums3和nums4组合,求出所有可能和;就退化为两数和问题,时O(n^2) 空O(n^2)——分组+哈希表
注:3 4 无需使用哈希表存 只要验证3 4 组合的相反数在1 2 的哈希表中是否出现即可 节省内存

代码

res = 0
hashmap = {}
# 1 2分为一组
for i in range(len(nums1)):
    for j in range(len(nums2)):
        a = nums1[i] + nums2[j]
        if a in hashmap:
            hashmap[a] += 1
        else:
            hashmap[a] = 1
# 3 4分为一组
for i in range(len(nums3)):
    for j in range(len(nums4)):  
        diff = -nums3[i]-nums4[j]
        if diff in hashmap:
            res = res + hashmap[diff]  # hashmap中数值为key, 出现的次数为val

return res


383 赎金信

题目

在这里插入图片描述

分析

若magazine 里的字符数大于等于ransonNote中字符 则为True

  1. 构建字典型哈希表,统计magazine中字符(key)和数量(val)
  2. 遍历ransoNote中元素,
    若元素在哈希表中,每出现一次则减1
    若元素不在哈希表中或在哈希表但val<0的,则false
    # 注:小写英文字符 可用ord(i)-ord(‘a’) 存在长26的数组

代码

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        # 小写英文字符  可用ord(i)-ord('a') 存在长26的数组   同242.有效的字母异位词
        # 以相对差做索引 出现次数做val magazine中出现一次val+1  ransomNote中出现一次val-1
        if len(magazine)<len(ransomNote):
            return False
        res = [0]*26
        for i in magazine:
            idx = ord(i)-ord('a')
            res[idx] += 1
        for i in ransomNote:
            idx = ord(i)-ord('a')
            res[idx] -= 1
            if res[idx] < 0 :
                return False
        return True

15 三数之和

题目

在这里插入图片描述
在这里插入图片描述

分析

只要求三元数组,不要求下标,可先转为有序数组,再先固定一个,另两个就是两数和问题 ——双指针
对同一指针的相同元素跳过,可实现去重

代码

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 所有和为 0 且不重复的三元组
        # 有序可以实现去重
        n = len(nums)
        res = []
        nums.sort()
        for i in range(n-2):
            if i>0 and nums[i]==nums[i-1]:
                continue  # 对i去重
            j = i+1
            k = n-1
            while j<k:
                if nums[i]+nums[j]+nums[k]<0:  # j右移
                    while j<k-1 and nums[j]==nums[j+1]:
                        j +=1
                    j+=1
                elif nums[i]+nums[j]+nums[k]>0:
                    while j<k-1 and nums[k]==nums[k-1]:
                        k -=1
                    k-=1
                else:
                    res.append([nums[i],nums[j],nums[k]])
                    while j<k-1 and nums[j]==nums[j+1]:
                        j +=1
                    while j<k-1 and nums[k]==nums[k-1]:
                        k -=1
                    j+=1
                    k-=1
        return res

18 四数之和

题目

在这里插入图片描述

分析

同15 三数和问题 这里固定两个指针

代码

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        res = []
        n = len(nums)
        if len(nums)<4:  # 剪枝
            return res
        nums.sort()
        for a in range(n-3):
            if a>0 and nums[a]==nums[a-1]:  # 剪枝 去重
                continue
            if sum(nums[a:a+4])>target:  # 剪枝
                return res
            for b in range(a+1,n-2):
                if b>a+1 and nums[b]==nums[b-1]:
                    continue
                c = b + 1
                d = n - 1
                while c<d:                       
                    if nums[a]+nums[b]+nums[c]+nums[d]<target:
                        c+= 1
                    elif nums[a]+nums[b]+nums[c]+nums[d]>target:
                        d-=1 
                    else:
                        res.append([nums[a],nums[b],nums[c],nums[d]])
                        while c<d-1 and nums[c]==nums[c+1]:
                            c+= 1  
                        while c<d-1 and nums[d]==nums[d-1]:
                            d-= 1  
                        c+=1
                        d-=1
        return res

注:# 数组切片nums[0:4] range(i+1,n-2) 一个冒号一个逗号 都是左闭右开

560 和为 K 的子数组

在这里插入图片描述
思路:统计前缀和,若两前缀和presum[i]和presum[j]的差为k,说明i+1到j这一段和为k

class Solution:
    def subarraySum(self, nums: List[int], k: int) -> int:
        # 前缀和 记录nums[0]到nums[i]的和
        mp = {}
        mp[0] = 1
        counter = 0
        presum = 0
        for i in range(len(nums)):
            presum += nums[i]  # nums[0]到nums[i]的和
            diff = presum - k  # 若想当前前缀和减i之前的前缀和为k,则i之前的前缀和应等于diff
            if diff in mp:  # 若diff在mp中,说明nums[i]之前有前缀和为diff的
                counter += mp[diff]  # mp[diff]为在当前presun下,其左边序列中前缀和等于diff的序列的数目
            if presum in mp:  # 别忘了将当前节点的前缀和也加入mp中
                mp[presum] += 1
            else:
                mp[presum] = 1
        return counter 

时,空O(N)

1647. 字符频次唯一的最小删除次数

在这里插入图片描述

class Solution:
    def minDeletions(self, s: str) -> int:
        # 用哈希表统计字符出现的次数  key为字符,val为次数
        # 再遍历一遍哈希表,将次数存入set中,若出现一个字符的次数已在set中,就将其次数减1再比较,直至不与set中元素重复且>=0,每次减1操作次数都加1
        hp1 = {}
        for i in s:
            if i in hp1:
                hp1[i] += 1
            else:
                hp1[i] = 1

        nums = set()  
        freq = 0  
        for val in hp1.values():  # 对所有出现过的字符遍历
            while val in nums:
                val -= 1  
                freq += 1
            if val > 0:
                nums.add(val)
        return freq 

128. 最长连续序列

在这里插入图片描述
思路:包含nums[i]的最长序列长度因为包含其左节点的最长序列长+包含其右结点的最长序列长度

class Solution:
    def longestConsecutive(self, nums: List[int]) -> int:
        hashmap = {}
        ans = 0
        for i in range(len(nums)):
            if nums[i] not in hashmap:
                left = hashmap.get(nums[i]-1, 0)  # 获取左右相邻字符串的长度
                right =hashmap.get(nums[i]+1, 0)
                length = left+right+1  # 包括nums[i]的序列的长度
                ans = max(length, ans)  # 更新最长序列长度
                hashmap[nums[i]] = length
                hashmap[nums[i]-left] = length  # 更新左右边界的序列长度 中间不用更新 之后不会使用                
                hashmap[nums[i]+right] = length
        return ans        

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值