算法打卡Day05

本文介绍了如何使用哈希表解决LeetCode中的四个问题:判断字母异位词、寻找两个数组交集、检查快乐数和寻找两数之和。通过实例演示了字典、数组和集合在这些问题中的应用,以及优化空间复杂度的方法。

今日任务:

1)哈希表理论基础

2)242.有效的字母异位词

3)349.两个数组的交集

4)202.快乐数

5)1.两数之和

242.有效的字母异位词

题目链接:242. 有效的字母异位词 - 力扣(LeetCode)

给定两个字符串 s 和 t ,编写一个函数来判断 t 是否是 s 的字母异位词。

注意:若 s 和 t 中每个字符出现的次数都相同,则称 s 和 t 互为字母异位词。

示例 1:

输入: s = "anagram", t = "nagaram"

输出: true

示例 2:

输入: s = "rat", t = "car"

输出: false

文章讲解:代码随想录 (programmercarl.com)

视频讲解:学透哈希表,数组使用有技巧!Leetcode:242.有效的字母异位词哔哩哔哩bilibili

思路:

这题是要判断两个字符串中字符是否一样,包括字符出现的次数

立马想到用哈希表解决,将其中一个字符串用一个哈希表存放

具体用什么哈希表,我们可以用字典,或者数组

1)字典

        遍历第一个字符串s,key为s中出现的字符,value为该字符出现的次数。

        遍历第二个字符串t,判断每个字符是否出现在字典中,若出现,则相应的value - 1,若没有则直接返回False

        最后遍历一边字典,当value出现了不为0的数,则返回False,顺利遍历完,则返回True

2)数组

        这里用数组与上面一样,我们定义一个26个元素的数组,表示26个字母,0-25分别表示a-z,这里比较巧妙的的是,字母的ascii值是连续的,我们只需要用字母的ascii值减去a的ascii值,就能使0-15分别与a-z对应起来

class Solution:
    def isAnagram(self, s: str, t: str) -> bool:
        # todo 1定义一个数组,其下标对应26个字母,其值对应字符串中这个字母出现的次数
        record = [0]*26

        # todo 2统计s字符串中的字母出现次数
        for i in s:
            # 如果a表示0,a-z的ascii值连续,所以用相应的字母减去"a",即可得到a-z对应的0-26
            record[ord(i)-ord('a')] += 1

        # todo 3统计t字符串中字母出现的次数,每出现一个在s的基础上相应的位置-1
        for i in t:
            record[ord(i)-ord('a')] -= 1

        # todo 4遍历数组,如果出现0意外的数,则表示这两个不是字母异位
        for i in record:
            if i !=0:
                return False

        return True

感想:

刚接触哈希表,对这个的使用还不是很熟练,看到一道题,不能立马联想到哈希表,还是题目做的不够多。这题还有一个比较巧妙的技巧,节省了内存空间,通常我们可能是将两个字符串转成哈希表,再比较,而这个只需转一个,另一个在上一个哈希表中做减法即可

时间复杂度:O(n)

空间复杂度:O(1)

349.两个数组的交集

题目链接:349. 两个数组的交集 - 力扣(LeetCode)

给定两个数组 nums1 和 nums2 ,返回 它们的交集 。输出结果中的每个元素一定是 唯一 的。我们可以 不考虑输出结果的顺序 。

示例 1:

输入: nums1 = [1,2,2,1] , nums2 = [2,2]

输出:[2]

示例 2:

输入: nums1 = [4,9,5] , nums2 = [9,4,9,8,4]

输出:[9,4]或[4,9]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:学透哈希表,set使用有技巧!Leetcode:349. 两个数组的交集哔哩哔哩bilibili

思路:

最简单的方法就是使用将两个列表转为集合,用&取两个集合交集(但这么做没什么意义)

我们换个思路

我们将其中一个数组专用哈希表存储,遍历另一个数组,判断里面的元素是否在哈希表中,如果在,则用集合保存(集合可以避免重复保存)

第一个数组转用哈希表存储,如果数组很大,那我们这里的哈希表可以选字典,如果这里的数组有限,不算大,也可以选数组做哈希表,数组哈希表只需比原数组的上限大一点点即可(以防万一)

方法一:使用字典和集合

class Solution:
    # 使用字典和合集
    def intersection(self, nums1: list[int], nums2: list[int]) -> list[int]:
        # todo 1)将某个数组中所有元素用哈希表字典存储
        table = {}
        for n in nums1:
            # table.get(n,0) 获取字典里key为n的值,如果没有该键,则返回0
            table[n] = table.get(n, 0) + 1

        # todo 2)遍历另一个数组,判断其是否在哈希表中,如果存在,则保存在集合中
        result = set()
        for n in nums2:
            if n in table:
                result.add(n)
                del table[n]  # 可加可不加,set本身就不重复保存

        return list(result)

使用集合改进:

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]:
        table = [0]*1005
        for i in nums1:
            table[i] += 1

        result = set()
        for i in nums2:
            if table[i] != 0:
                result.add(i)
        return list(result)

感想:

这题可以转换成查找某个数在数组中是否存在,对于这样的问题我们可以毫不犹豫的选择哈希表

202.快乐数

题目链接:202. 快乐数 - 力扣(LeetCode)

编写一个算法来判断一个数 n 是不是快乐数。

「快乐数」 定义为:

对于一个正整数,每一次将该数替换为它每个位置上的数字的平方和。 然后重复这个过程直到这个数变为 1,也可能是 无限循环 但始终变不到 1。 如果这个过程 结果为 1,那么这个数就是快乐数。 如果 n 是 快乐数 就返回 true ;不是,则返回 false 。

示例 1:

输入:n = 19

输出:true

解释: 1^2 + 9^2 = 82

            8^2 + 2^2 = 68

            6^2 + 8^2 = 100

            1^2 + 0^2 + 0^2 = 1

示例 2:

输入:n = 2

输出:false

文章讲解:代码随想录 (programmercarl.com)

思路:

这题有两个核心点

1)找到数的每一位

2)这题可能会无限循环下去,但我们不能让其无限循环下去,我们要判定什么时候开始了无线循环,并终止循环。其实出现无线循环的点,就是我们求的平方和开始出现了重复。

弄清楚这两点后面的就好做了

方法一:采用数学取余的方式得到该数的每一位,计算平方和并用集合收集

class Solution:

    def isHappy(self, n: int) -> bool:
        # todo 2.将平方和用set收集起来,一段出现重复则返回false,或当平方和为1时返回true
        res = set()

        while True:
            n = self.getSum(n)   # 注意这里不能新定义变量,这里是将计算的和作为下一次的数继续求平方和
            if n == 1:
                return True

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

    # todo 1.采用数学取余的方式得到数的每一位,并计算平方和
    def getSum(self, n: int) -> int:
        sqraSum = 0
        # i = 0
        while n:
            n, r = divmod(n, 10)
            sqraSum += r ** 2

        return sqraSum

方法二:将数字转换为字符串,遍历得到该数的每一位,计算平方和并用集合收集

class Solution:
    def isHappy(self, n: int) -> bool:
        res = set()
        while n not in res:  # 判断n是否在集合中,不在则添加到集合中,集中有没有初始n本身并不影响
            res.add(n)

            # 将数字 n 转换成字符串
            n_str = str(n)

            sum = 0
            for i in n_str:
                sum += int(i) ** 2
            if sum == 1:
                return True
            else:
                n = sum

        return False

感想:

这题在第一个方法中,出现了问题,在n = self.getSum(n)  中我定义了一个新变量去赋值,导致第二次还是计算的n一下就退出循环了,debug每一个变量才发现问题所在。这完全是粗心导致的,找问题花费了大量时间。

1.两数之和

题目链接:1. 两数之和 - 力扣(LeetCode)

给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那两个整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。

示例 1:

输入: nums = [2,7,11,15], target = 9

输出:[0,1]

解释:因为 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

示例 2:

输入: nums = [3,2,4], target = 6

输出:[1,2]

示例 3:

输入: nums = [3,3], target = 6

输出:[0,1]

文章讲解:代码随想录 (programmercarl.com)

视频讲解:梦开始的地方,Leetcode:1.两数之和,学透哈希表,map使用有技巧!哔哩哔哩bilibili

思路:

最简单的方法,暴力法,for循环遍历数组,找出该指针之后是否有值与当前指针相加为target值,有则返回

还有一个方法就是采用哈希表改进,这里选用字典作为哈希表,采用字典的key保存数组的元素,而value保存数组的下标,遍历数组,直接查找字典中是否存在与当前指针的差值,存在返回下标即可

方法一:暴力法

class Solution:
    # 暴力法,两层循环
    def twoSum(self, nums: list[int], target: int):
        for i in range(len(nums)):
            div = target - nums[i]
            for j in range(i+1,len(nums)):
                if nums[j] == div:
                    return [i,j]

方法二:采用哈希表(字典)改进

class Solution:
    # 方法二:采用哈希表(字典)改进
    def twoSum(self, nums: list[int], target: int):
        res = dict()
        for i,num in enumerate(nums):
            div = target - num
            if div in res:
                return [res[div],i]
            res[num] = i

        return []

感想:

在方法二中,我曾陷入了一个坑,我最初的想法是直接把一个数组转为字典哈希表存储,但在写代码过程中,我发了问题,key是不能重复的,而数组中的数是可能重复的,我也不能直接去重(原因见案例示例3),但我必须要把数组元素存为key才能更方便查找,到这里思路就卡了。

而上面代码很巧妙的解决了这个问题,我们将数组转为字典的过程中,同时把其他的部分也判断了。遍历过程中,核心是将已经遍历过的元素添加到哈希表中,同时,在已经遍历过的元素中寻找是否有当前指针的差值,这样完美解决了我刚刚说的问题(相同的数相加得到目标值,这里相同数并不是同一个位置的元素,只是值相同),以及规避了自己不能与自己想加的问题(我是在遍历过的数中找差值,没有包含档前指针,所以不存在自己与自己相加的问题)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值