代码随想录算法训练营第五天|哈希表理论基础、242.有效的字母异位词、349. 两个数组的交集、202. 快乐数、1. 两数之和

写代码的第五天

哈希表的理论基础

1、当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法。
2、常见的哈希结构:数组,set,map
3、std::unordered_set底层实现为哈希表,std::set 和std::multiset 的底层实现是红黑树,红黑树是一种平衡二叉搜索树,所以key值是有序的,但key不可以修改,改动key值会导致整棵树的错乱,所以只能删除和增加。
在这里插入图片描述
std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。
在这里插入图片描述

242.有效的字母异位词

第一想法(没改出来)

s和t分别遍历一遍,计数,要是出现的每个字符数量都相等那么就返回true,否则返回false

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        strall = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
        setst = {}
        for i in strall:
            count = 0
            for j in s:
                if j == i:
                    count += 1
            setst[j] = count
        for k in setst.keys():
            for j in t:
                if j == k:
                    setst[k] -= 1
        for k in setst.keys():
            if setst[k] != 0:
                return True
        return False

第一个错误的地方是setst[j] = count,这句话已经跳出j这个循环之外了,j就不对了,应该是i;
第二个错误的地方是最后的判断,全为零才是true,只要有不为零的就是false。
正确代码

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        strall = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z']
        setst = {}
        for i in strall:
            count = 0
            for j in s:
                if j == i:
                    count += 1
            setst[i] = count
        for k in setst.keys():
            for j in t:
                if j == k:
                    setst[k] -= 1
        for k in setst.keys():
            if setst[k] != 0:
                return False
        return True

一定能做出来的思路

用数组实现哈希法。首先要初始化一个数组,长度是多少?
解决问题1:如果用数组的话怎么能知道每个字母出现几次呢?很容易想到的是如果用字典的话,key代表字母,value代表在这个字符串中这个字母出现的次数,但是如果用数组的话能用上的只有数组的下标和数组下标表示的位置的值,所以可以看出是要用数组下标和数组下标位置对应的值来代表字母和字母出现的次数。
解决问题2:是用数组下标代表字母还是字母出现的次数呢?我们的第一想法肯定是下标本身就是数字表示的,字母出现次数也是数字表示,所以用下标来代表字母出现次数,但是有个问题如果这个字母出现了100000次,那这个数组你要定义多大呢?这是不可控的,而我们知道只有当数组长度是可控的时候才能用数组实现哈希,那么这个里面我们要比较的是字母,字母一共就26个,这个长度是可控的,所以用数组下标代表字母,用数组下标表示的位置的值代表字母出现的次数。
解决问题3:如何用数组下标代表字母?在这个里面我们涉及到了ascii码的问题,从a-z在ascii码中是连续的,并且是数值表示的,所以我们可以用[a-z]对应的ascii数值表示数组下标。
解决问题4:数组下标是从0开始的,可是a-z的ascii码可不是从0开始的,应该怎么解决?将数组变成[a-‘a’~z-‘a’],在这个表示里面’a’代表的是a的ascii码,也就是说不论是哪个字母ascii码都减去a的ascii码,这样就得到了从0开始的数组。
错误版本一:python中不允许出现字符-字符的操作,所以应该是ord(字符)-ord(字符)。

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

正确版代码

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

349. 两个数组的交集

第一想法纯暴力啊咱就是说,一点哈希也不沾

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        result = []
        for i in range(len(nums1)):
            for j in range(len(nums2)):
                if nums1[i] == nums2[j]:
                    result.append(nums1[i])
        return list(set(result))

我的脑子你去哪里了~~~~

用set解决问题

思路:先设置一个哈希set,用于存储最后的结果。将第一个数组转化为哈希表,然后遍历第二数组的每一个元素,与哈希表中元素进行对比,查看是否出现过,如果出现过那么就将这个值放到最后输出的集合中。
解决问题1:如何将数组一转化为哈希表?python中的语法是setnums = set(nums1)
解决问题2:如何查看数字是否出现过?使用for循环遍历nums2,并查看nums2中的数字是在setnums中,如果在就将其加入到最后返回结果的set中。
解决问题3:为什么返回结果我们要设置为set集合?可以不用去重,set结构本身就不会出现重复。所以设置result = set()
解决问题4:集合该怎么加入元素?python中的语法是result.add(nums2[i])。
错误第一版:报错原因是最后的输出值应该是int型,长这样[2],而我输出的是集合,长这个样子{2}.

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        setnums = set(nums1)
        result = set()
        for i in range(len(nums2)):
            if nums2[i] in setnums:
                result.add(nums2[i])
        return result

在这里插入图片描述
正确版代码

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        setnums = set(nums1)
        result = set()
        for i in range(len(nums2)):
            if nums2[i] in setnums:
                result.add(nums2[i])
        return list(result)

总的来说这道题对我最大的难度是我不熟悉set的语法,都是现查的,这部分要补一补。

用数组解决

因为题中给了一些限制,数组num的长度以及里面的值,所以可以用数组解决。
思路:先设置一个数组,用于存储最后的输出,由于题中给的限制0 <= nums1[i], nums2[i] <= 1000,所以可以设置一个长度为1005的哈希数组进行存放nums1的数字情况,然后遍历nums2数组与哈希数组中的值进行比较,如果出现一致的,那么就把这个数值加入到最后输出结果中。
解决问题1:nums1的数字情况存储成什么样子?还是那个问题数组的下标和下标对应位置的值应该分别存储什么?哈希数组设置的长度是1005,因为题中给的限制0 <= nums1[i], nums2[i] <= 1000,也就是说我们把nums1和nums2的数组中的数字作为哈希数组的下标,所以下标对应的位置的值我们应该做一个标记,证明这个数字在nums1数组中出现了,在这里我们把这个标记设置为1(设置什么都行)。
解决问题2:其实在这个里面容易出错的就是nums2数组中的数值对应的是hashnums中的数组下标!!!if hashnums[nums2[i]] == 1:
错误版本一:原因是最后输出的是列表,但是列表并没有去重的功能,所以要把列表转换为set进行去重,然后在转换为list满足输出条件。

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        result = []
        hashnums = [0] * 1005
        for i in range(len(nums1)):
            hashnums[nums1[i]] = 1
        for i in range(len(nums2)):
            if hashnums[nums2[i]] == 1:
                result.append(nums2[i])
        return list(set(result))

在这里插入图片描述
正确代码

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        result = []
        hashnums = [0] * 1005
        for i in range(len(nums1)):
            hashnums[nums1[i]] = 1
        for i in range(len(nums2)):
            if hashnums[nums2[i]] == 1:
                result.append(nums2[i])
        return list(set(result))

202. 快乐数

完全没思路,觉得应该是把这个数的每个位变成列表,然后不停循环的求各个位置的数字平方和,如果是1,停止,返回true,否则返回false。但是一旦没有1出现,那就是死循环。。。

思路

第一步把数字进行各数位平方求和,第二步把每个和都放到哈希表里,第三步查看将要插入的数字是否在哈希表中存在,若存在直接返回false,若为1直接返回true,其他的情况就是把和加入到哈希表中。
错误代码第一版:超出内存限制。错误原因是对于各个数位求平方和错误,在这段代码中只求了最末尾的平方和。

class Solution:
    def isHappy(self, n: int) -> bool:
        hashtable = set()
        sums = 0
        while n:
            sums += (n % 10) ** 2
            if sums == 1:
                return True
            if sums in hashtable:
                return False
            hashtable.add(sums)
            n = sums
        return False

正确代码

class Solution:
    def isHappy(self, n: int) -> bool:
        hashtable = set()
        while n:
            sums = self.getSum(n)
            if sums in hashtable:
                return False
            if sums == 1:
                return True
            hashtable.add(sums)
            n = sums
        return False
        
    
    def getSum(self,n):
        sums = 0
        while n:
            sums += (n % 10) ** 2
            n = n // 10
        return sums

1. 两数之和

暴力解法

哈哈哈哈希表是一点没用上,我真无语啊啊啊啊啊

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            for j in range(i+1,len(nums)):
                if nums[i] + nums[j] == target:
                    return (i,j)
        return 

在这道题中我能理解的哈希表就是,for循环一层,然后用target-nums[i]的数值在哈希表中进行比较,看这个值是否出现。

用集合实现

错误版本一:戛然而止写不下去的代码,因为最后发现要返回的是下标位置,我不会写,去查了一下。nums.index(complement) 是 Python 中列表(list)的一个方法调用语法。它用于查找列表 nums 中第一次出现指定元素 complement 的索引位置,并将该索引作为结果返回。具体来说,index() 方法是列表对象的一个方法,用于查找指定元素在列表中的索引位置。

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = set(nums)
        for i in range(len(nums)):
            hashtable.discard(nums[i])
            x = target - nums[i]
            if x in hashtable:
                return (nums.index(x),i)
        return

看起来逻辑很合理,但是对于这样的测试用例是无法通过的,因为最开始把nums变成set形式,就已经把里面的数据去重了。

在这里插入图片描述
通过下面map的方法,有了一点启发,发现如果nums数组变成我们要的集合,然后再查找,会产生一个情况,相同数字只剩一个,已经把原始的数组改变了,数值已经发生变化了。
正确代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = set()
        for i in range(len(nums)):
            x = target - nums[i]
            if x in hashtable:
                return (nums.index(x),i)
            hashtable.add(nums[i])
        return

在这段代码中的set数组存储的也是之前见过遍历过的数据,这样在遍历新的数据的时候可以在set中查找之前是否存在过,所以hashtable.add(nums[i])。

用map实现

思路和上面是一样的,只不过设置的哈希表不同,存放的内容不同。
解决问题1:输出的是数组的下标,同时还要计算数组中数字的和,所以我们需要同时知道这两个值,那么可以用字典来表示,字典中有key和value,哪个是key,哪个是value?我们需要知道的是数字和数字相加的和是不是等于target,那么就需要判断数字在不在,根据这个数字去找他的下标,在字典中我们是根据key找value,所以key是存放数字的,value是存放下标。
解决问题2:这个字典中存放的究竟是什么?字典中存放的是我们已经查询过的但是并没有出现和为target的数,这样在循环到下一个数字时,我们可以在这个字典中找有没有能和他匹配的数字,如果有,直接输出,如果遍历之后没有那么失败。
正确代码

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashtable = {}
        for i in range(len(nums)):
            x = target - nums[i]
            if x in hashtable:
                return (i,hashtable[x])
            hashtable[nums[i]] = i
        return 

在这里面为什么不直接nums数组变成我们要的字典,然后再查找?因为题中给了条件:数组中同一个元素在答案里不能重复出现。

总结

1、哈希表范围可控的时候用数组解决问题。
2、哈希表中值比较少但是又比较分散,这种情况用set解决。
3、学了好多set,dict的语法规则,啊啊啊啊都忘没了啊啊啊啊
1️⃣、for k in setst.keys():setst[k] -= 1 setst.keys()代表找到集合的key
2️⃣、python中不允许出现字符-字符的操作,所以应该是ord(字符)-ord(字符)。
3️⃣、 result = set() 建立集合
result.add(nums2[i]) 在集合中添加元素
4️⃣nums.index(complement) 是 Python 中列表(list)的一个方法调用语法。它用于查找列表 nums 中第一次出现指定元素 complement 的索引位置,并将该索引作为结果返回。具体来说,index() 方法是列表对象的一个方法,用于查找指定元素在列表中的索引位置。

  • 29
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值