代码随想录第六天|242.有效的字母异位词,349.两个数组的交集,202.快乐数,1.两数之和

哈希表理论基础

文章讲解:https://programmercarl.com/%E5%93%88%E5%B8%8C%E8%A1%A8%E7%90%86%E8%AE%BA%E5%9F%BA%E7%A1%80.html

哈希表是根据关键码的值而直接进行访问的数据结构。

index=hashFunction(name)

如果两个内容都映射到了同一个索引下标,就是哈希碰撞。一般哈希碰撞有两种解决方法, 拉链法和线性探测法。拉链法就是直接外挂在上面,线性探测法是依靠哈希表中的空位来解决碰撞问题。

常见的三种哈希结构是数组、set(集合)和map(映射)。

std::unordered_map 底层实现为哈希表,std::map 和std::multimap 的底层实现是红黑树。同理,std::map 和std::multimap 的key也是有序的(这个问题也经常作为面试题,考察对语言容器底层的理解)。

当我们要使用集合来解决哈希问题的时候,优先使用unordered_set,因为它的查询和增删效率是最优的,如果需要集合是有序的,那么就用set,如果要求不仅有序还要有重复数据的话,那么就用multiset。map 是一个key value 的数据结构,map中,对key是有限制,对value没有限制的,因为key的存储方式使用红黑树实现的。

虽然std::set、std::multiset 的底层实现是红黑树,不是哈希表,std::set、std::multiset 使用红黑树来索引和存储,不过给我们的使用方式,还是哈希法的使用方式,即key和value。所以使用这些数据结构来解决映射问题的方法,我们依然称之为哈希法。 map也是一样的道理。

总结一下,当我们遇到了要快速判断一个元素是否出现集合里的时候,就要考虑哈希法

但是哈希法也是牺牲了空间换取了时间,因为我们要使用额外的数组,set或者是map来存放数据,才能实现快速的查找。

242. 有效的字母异位词

看起来挺简单的,我的第一反应是用字典存储每个字母出现的次数,然后对比两个字典是不是相同。。但是听起来好像有点傻,而且这也不是哈希表?如果用哈希表的话,是不是字母填在哈希表上,次数挂在上面?

直接看题解了:https://www.bilibili.com/video/BV1YG411p7BA/?vd_source=40781b3117fa72cc67da258629107b62

这里又提到暴力解法了,我发现每次提到暴力,都先要确定是几组for循环,我好像每次都有点写不清楚暴力的细节,以后要加强这方面练习!

题解里讲到是要定义一个长度为26的数组,然后统计第一个字符串中字母出现的频率,减去第二个字符串中字母出现的频率。这里还有一个之前不知道的知识点,就是a-z的ASCII码是连续的,所以可以用数字减ord(a),ord() 是一个 Python 内置函数,它用于返回一个字符的 ASCII 值或 Unicode 值(如果字符是 Unicode 的话)。这样操作,但是总感觉还有点魔幻,直接让我写代码我不敢写啊!去看答案了。文字答案在这:https://programmercarl.com/0242.%E6%9C%89%E6%95%88%E7%9A%84%E5%AD%97%E6%AF%8D%E5%BC%82%E4%BD%8D%E8%AF%8D.html#%E5%85%B6%E4%BB%96%E8%AF%AD%E8%A8%80%E7%89%88%E6%9C%AC

代码部分在这里,ord(i)-ord("a")就是字母相对于a的偏移量,然后字母在s字符串出现一次就在该索引上+1,字母在t字符串上出现一次就在该索引上-1,只有所有元素全为0才是字母异位词。原理还是很简单的~

class Solution(object):
    def isAnagram(self, s, t):
        """
        :type s: str
        :type t: str
        :rtype: bool
        """
        record = [0]*26

        for i in s:  #对于s的每个字母
            record[ord(i)-ord("a")] +=1
        for i in t:
            record[ord(i)-ord("a")] -=1
        
        for i in range(26): # 这里遍历整个字母表,保证稳妥
            if record[i]!=0:
                return False

        return True

349. 两个数组的交集

这个题一眼看过去好像和上一道题有点类似,但是由于不计顺序,所以用列表好像不行。于是我去看了题解:

视频版:

https://www.bilibili.com/video/BV1ba411S7wu/?vd_source=40781b3117fa72cc67da258629107b62

文字版:https://programmercarl.com/0349.%E4%B8%A4%E4%B8%AA%E6%95%B0%E7%BB%84%E7%9A%84%E4%BA%A4%E9%9B%86.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE

我发现,这涉及一种哈希结构unordered_set,和我一开始想的一样。这种数据结构的底层是哈希值。我们其实先遍历num1放到哈希表里,然后遍历num2,看里面每个元素是否出现在哈希表里,如果出现过,就把这个元素放到result里。

这道题我们用set解决,我感觉距离写代码主要的困难在于,我好像不知道用python怎么写unordered_set,所以还是直接看解法了。我发现这个里面,table相当于哈希表,是用来给元素计数的,用的是字典格式,遍历nums1,对于每个元素num,如果它已经存在于table中,则将其对应的值加1;如果不存在,则将其初始值设为0。res相当于上一段说到的result。然后对我来说比较难懂的一个点是table.get(num,0)为什么要加1,问了Claude,他说num如果在table里就会返回当前已经出现过的次数,+1就是把这一次也加上。

class Solution(object):
    def intersection(self, nums1, nums2):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :rtype: List[int]
        """
        table={}
        for num in nums1: # 第一个循环,统计num1中每个元素出现的次数
            table[num]=table.get(num,0)+1
        
        res = set()
        for num in nums2:
            if num in table: # 检查一个键是否在字典中,另一种方式就是get函数
                res.add(num)
                # del table(num) # 确保只出现一次,但是我发现这一行注释掉也没问题

        return list(res)

还有一种数组解法。建立一个长度为1001的哈希数组,然后把num1的所有数字出现次数设定为1,再遍历num2,判断它有没有在这个哈希数组出现过。后来我看1001是因为力扣给了num的范围<1000。

class Solution:
    def intersection(self, nums1: List[int], nums2: List[int]) -> List[int]:
        count1 = [0]*1001
        count2 = [0]*1001
        result = []
        for i in range(len(nums1)):  # 给num1和num2分别一个循环,用户计数
            count1[nums1[i]]+=1
        for j in range(len(nums2)):
            count2[nums2[j]]+=1
        for k in range(1001):
            if count1[k]*count2[k]>0: # 在count1和count2都出现了就记录在result里
                result.append(k)
        return result

还有一个很绝的,也是我一开始想到的解法哈哈哈,用到set用&取交集就行了。

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

202. 快乐数

 这个题一眼看过去好像有点复杂,但我看题目介绍里说这道题和上一道差不多,而且我连视频题解都妹找到,有点疑惑。

文字教程:https://programmercarl.com/0202.%E5%BF%AB%E4%B9%90%E6%95%B0.html

原来,题目中说了会 无限循环,那么也就是说求和的过程中,sum会重复出现,这对解题很重要!

于是去看源码了,自己解读一下:
 

class Solution(object):
    def isHappy(self, n):
        """
        :type n: int
        :rtype: bool
        """
        record = set()
        while True:  # while True是为了检测无限循环的情况,因为不知道循环几次才得到1,或者永远得不到1
            n=self.get_sum(n)
            if n ==1:
                return True
            if n in record: # 如果数字重复出现了,可以预计就会产生无限循环
                return False
            else:
                record.add(n) # 出现过的数字都在集合里记录下来


    def get_sum(self, n):
        new_num = 0
        # 从num最低一位开始循环,假设num是134,第一轮n和r就是13和4,第二轮是1和3,第三轮是0和1。以此类推,所以r是我们要平方加和的数
        while n:  # 这里判断的标准是n不为0
            n, r = divmod(n, 10) #表达除法,输入是被除数和除数,输出是商和余数
            new_num += r**2 # 把每一位平方加和
        return new_num

然后又看到了第二种解法,这里是先设定一个while无限循环,看数字在record里有没有出现过(也对,出现过就可以直接排除了),再看平方和是不是等于1。然后这里平方和是通过字符串转换实现的。同理,这里record也可以直接换成数组,也成立。

class Solution:
    def isHappy(self, n: int) -> bool:
        # 创建一个集合用于记录出现过的数字
        record = set()

        # 使用while循环,当n不在record集合中时执行循环体
        while n not in record:
            # 将当前的n加入record集合
            record.add(n)

            # 初始化new_num为0,用于存储各位数字平方和
            new_num = 0

            # 将n转换为字符串,方便遍历每一位数字
            n_str = str(n)

            # 遍历n_str的每一个字符(数字)
            for i in n_str:
                # 将当前数字转换为整数,计算平方后累加到new_num
                new_num += int(i) ** 2

            # 如果new_num等于1,说明n是快乐数,返回True
            if new_num == 1: return True

            # 否则,将n更新为new_num,进入下一次循环
            else: n = new_num

        # 如果退出while循环,说明出现了循环,n不是快乐数,返回False
        return False

后面居然还有三种解法,累了……不看了先

1.两数之和

这个题第一眼看过去好像暴力解法写两个循环遍历就行?

力扣原题:https://leetcode.cn/problems/two-sum/

视频讲解:https://www.bilibili.com/video/BV1aT41177mK/很神奇,老师说这道题是用map来解决的,和我想的好像不太一样。老师说,当需要判断一个元素是否出现过时,第一反应应该是哈希表。这个题在遍历的时候,需要判断一个元素是否之前遍历过(比如遇到了2,就想找到7是否遍历过)。选择哈希表结构方面,由于需要同时存储数组和数组下标,可以用map来存放。

听了一下解题思路感觉挺简单的,后面部分没听完就写出来了!

class Solution(object):
    def twoSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[int]
        """
        record = dict()  # 我发现我的第一反应是{},以后要记得用dict()
        for index, value in enumerate(nums): #index和value一起遍历要用enumerate
            if target-value in record:
                return [record[target-value],index]
            record[value]=index
        return [] # 如果没有找到就返回空列表

感觉应该要总结一下,用数组和/set/map做哈希映射的适用范围和具体情况。

文字讲解:

https://programmercarl.com/0001.%E4%B8%A4%E6%95%B0%E4%B9%8B%E5%92%8C.html

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值