leetcode-哈希表【三刷done】


一般哈希表都是用来快速判断一个元素是否出现集合里。
使用数组和set来做哈希法的局限:

  • 数组的大小是受限制的,而且如果元素很少,而哈希值太大会造成内存空间的浪费。
  • set是一个集合,里面放的元素只能是一个key。
  • map是一种key value的存储结构,可以用key保存数值,用value在保存数值所在的下标。

1、有效的字母异位词(easy排序或哈希)

242 有效的字母异位词
因为字符a到字符z的ASCII是26个连续的数值,所以字符a映射为下表0,相应的字符z映射为下表25。
再遍历 字符串s的时候,只需要将 s[i] - ‘a’ 所在的元素做+1 操作即可,并不需要记住字符a的ASCII,只要求出一个相对数值就可以了。 这样就将字符串s中字符出现的次数,统计出来了。
那看一下如何检查字符串t中是否出现了这些字符,同样在遍历字符串t的时候,对t中出现的字符映射哈希表索引上的数值再做-1的操作。
那么最后检查一下,record数组如果有的元素不为零0,说明字符串s和t一定是谁多了字符或者谁少了字符,return false。
最后如果record数组所有元素都为零0,说明字符串s和t是字母异位词,return true。

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        if len(s) != len(t):
            return False

        records = [0]*26
        ##并不需要记住字符a的ASCII,只要求出一个相对数值就可以了
        for i in s:
            records[ord(i) - ord('a')] += 1
            
        for i in t:
            records[ord(i) - ord('a')] -= 1
            
        return records == [0]*26

2、两个数组的交集(easy)

349 两个数组的交集

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        s1 = set(nums1)
        s2 = set(nums2)

        res = list()
        for i in s1:
            if i in s2:
                res.append(i)
        return res

3、快乐数(not done)

202 快乐数
题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!使用哈希法,来判断这个sum是否重复出现,如果重复了就是return false, 否则一直找到sum为1为止。

class Solution:
    def isHappy(self, n: int) -> bool:
        def calculate_happy(num):
            sum_ = 0
            
            # 从个位开始依次取,平方求和
            while num:
                sum_ += (num % 10) ** 2
                num = num // 10
            return sum_

        # 记录中间结果
        record = set()

        while True:
            n = calculate_happy(n)
            if n == 1:
                return True
            
            # 如果中间结果重复出现,说明陷入死循环了,该数不是快乐数
            if n in record:
                return False
            else:
                record.add(n)

4、两数之和(easy)

1 两数之和

class Solution:
    #哈希法
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        records = dict()

        # 用枚举更方便,就不需要通过索引再去取当前位置的值
        for idx, val in enumerate(nums):
            if target - val not in records:
                records[val] = idx
            else:
                return [records[target - val], idx] # 如果存在就返回字典记录索引和当前索引

5、四数相加ii(done)

454 四数相加II
我们可以将四个数组分成两部分,A 和 B 为一组,C 和 D 为另外一组。
对于 A 和 B,我们使用二重循环对它们进行遍历,得到所有 A[i]+B[j]的值并存入哈希映射中。对于哈希映射中的每个键值对,每个键表示一种 A[i]+B[j],对应的值为 A[i]+B[j]出现的次数。
对于 C 和 D,我们同样使用二重循环对它们进行遍历。当遍历到 C[k]+D[l]时,如果 -(C[k]+D[l]) 出现在哈希映射中,那么将 -(C[k]+D[l])对应的值累加进答案中。
最终即可得到满足 A[i]+B[j]+C[k]+D[l]=0的四元组数目。

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        hashmap = dict()
        for n1 in nums1:
            for n2 in nums2:
                if n1 + n2 in hashmap:
                    hashmap[n1+n2] += 1
                else:
                    hashmap[n1+n2] = 1
        
        # if the -(a+b) exists in nums3 and nums4, we shall add the count
        count = 0
        for n3 in nums3:
            for n4 in nums4:
                key = - n3 - n4
                if key in hashmap:
                    count += hashmap[key]
        return count

6、赎金信(easy)

383 赎金信
统计字符串ransomNote, magazine字符出现的次数,ransomNote的每个字符在magazine中,且个数小于等于magazine中该字符的个数,则return True

def canConstruct(ransomNote, magazine):
    arr = [0] * 26
    #通过arr记录 magazine里各个字符出现次数
    for i in magazine:
        arr[ord(i) -ord('a')] += 1
    
    for i in ransomNote:
    	#如果等于零说明ransomNote里出现的字符,magazine没有
        if arr[ord(i) - ord('a')] == 0:
            return False
        #遍历ransomNote,在arr里对应的字符个数做-操作
        else:
            arr[ord(i) - ord('a')] -= 1
    return True

7、三数之和(done)

15 三数之和
首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
依然还是在数组中找到 abc 使得a + b +c =0,我们这里相当于 a = nums[i] b = nums[left] c = nums[right]。
接下来如何移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。
如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        ans = []
        n = len(nums)
        nums.sort()
        for i in range(n):
            left = i + 1
            right = n - 1
            if nums[i] > 0:  #第一个数就大于0了,不存在和为0
                break
            if i >= 1 and nums[i] == nums[i - 1]:    #去重处理,仅对第一个数i
                continue
            while left < right:
                total = nums[i] + nums[left] + nums[right]
                if total > 0:
                    right -= 1
                elif total < 0:
                    left += 1
                else:
                    ans.append([nums[i], nums[left], nums[right]])
                    while left != right and nums[left] == nums[left + 1]: left += 1    #对第二个数做去重
                    while left != right and nums[right] == nums[right - 1]: right -= 1  #对第三个数做去重复
                    left += 1  
                    right -= 1
        return ans

8、四数之和(done)

18 四数之和
不要判断nums[k] > target 就返回了,三数之和 可以通过 nums[i] > 0 就返回了,因为 0 已经是确定的数了,四数之和这道题目 target是任意值。
四数之和的双指针解法是两层for循环nums[k] + nums[i]为确定值,依然是循环内有left和right下标作为双指针,找出nums[k] + nums[i] + nums[left] + nums[right] == target的情况。

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        n = len(nums)
        nums.sort()
        ans = []

        for i in range(n):  # 第一个数去重
            if i > 0 and nums[i] == nums[i - 1]: continue
            for j in range(i + 1, n):  # 第二个数去重
                if j > i + 1 and nums[j] == nums[j-1]: continue

                left = j + 1
                right = n - 1
                while left < right:
                    total = nums[i] + nums[j] + nums[left] + nums[right]
                    if total > target:
                        right -= 1
                    elif total < target:
                        left += 1
                    else:
                        ans.append([nums[i], nums[j], nums[left], nums[right]])
                        while left < right and nums[left] == nums[left + 1]: left += 1
                        while left < right and nums[right] == nums[right - 1]: right -= 1
                        left += 1
                        right -= 1
        return ans
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值