代码随想录 Day 7 | 454.四数相加II、383. 赎金、15. 三数之和 、18. 四数之和

一、454.四数相加II

建议:本题是 使用map 巧妙解决的问题,好好体会一下 哈希法 如何提高程序执行效率,降低时间复杂度,当然使用哈希法 会提高空间复杂度,但一般来说都是舍空间换时间, 工业开发也是这样。

题目链接/文章讲解/视频讲解:代码随想录

1. 看到这道题的第一想法

        暴力解法:四层for循环嵌套遍历

 2. 看完代码随想录的想法

(1)整体思路:

        首先,将四个数组划分为两两一组,比如nums1和nums2一组、nums1和nums2一组;

        然后,对其中一组进行遍历,计算元素加和,判断是否在哈希表里,如果不在就将加和存入哈希表,如果以及存在哈希表,那么就将计数加1,也就是加和作为下标index,计数作为value,记录这个加和出现了几次;

        接下来,对另一组进行遍历,将count计数初始化为0:由于四数相加等于0,那么这一组的两数相加应该等于前一组两数相加的相反数,每次遍历需要判断,新的index是否已经在哈希表出现,如果出现了需要在原总和上加上原总和。

        最后返回count就是有多少个元组可以满足四数相加为0。

(2)代码实现:

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        hashmap = dict()
        for i in nums1:
            for j in nums2:
                if i+j in hashmap:
                    hashmap[i+j] += 1
                else:
                    hashmap[i+j] = 1
        count = 0
        for i in nums3:
            for j in nums4:
                key = -i -j
                if key in hashmap:
                    count += hashmap[key]
        return count

3. 实现过程中遇到的困难及解决:

(1)要特别注意,map的实现方式在python中是dictionary,因为需要同时存key和value,而集合set只能存value

(2)字典的定义方式是dict(),而不是dic()!!

(3)本题目不需要去重,所以最后在第二组遍历时,需要count+=hashmap[key]

二、383. 赎金信

建议:本题 和 242.有效的字母异位词 是一个思路 ,算是拓展。

题目链接/文章讲解:代码随想录

 1. 看到这道题目第一想法

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        record = [0]*26
        for i in ransomNote:
            record[ord(i)-ord('a')] += 1
        for i in magazine:
            record[ord(i)-ord('a')] -= 1
        for i in range(len(record)):
            if record[i]>0:
                return False
        return True
        

        思路同242.有效的字母异位词,方法相同,已AC。

2. 看完代码随想录的想法

        使用Counter法:

from collections import Counter
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        return not Counter(ransomNote)-Counter(magazine)

(1)使用 Counter 类从 collections 模块中,对 ransomNote 和 magazine 字符串中的字符进行计数。Counter 会返回一个字典,其中键是字符串中的字符,值是该字符在字符串中出现的次数。

(2)然后,使用 - 运算符对两个 Counter 对象进行减法操作。这个操作会返回一个包含所有在 ransomNote 中但不在 magazine 中有足够数量字符的字典(如果有的话),或者如果 ransomNote 中的所有字符都可以在 magazine 中找到足够的数量,则返回一个空的 Counter 对象。

(3)最后,通过 not 运算符检查减法操作的结果。如果结果为空(即 ransomNote 中的所有字符都可以在 magazine 中找到足够的数量),not 运算符会将这个结果转换为 True,表示可以构造赎金信。如果结果不为空(即至少有一个字符在 magazine 中的数量不足以满足 ransomNote 的需求),not 运算符会将其转换为 False,表示无法构造赎金信。

三、15. 三数之和

建议:本题虽然和 两数之和 很像,也能用哈希法,但用哈希法会很麻烦,双指针法才是正解,可以先看视频理解一下 双指针法的思路,文章中讲解的,没问题 哈希法很麻烦。

题目链接/文章讲解/视频讲解:代码随想录

1. 看完代码随想录的想法

 (1)总体思路:

        首先对数组进行排序,然后 i 初始化指向数组第一个元素,然后定义两个指针Left和right,将其分别初始化为left = i+1,right = len(nums)-1。

        然后使用这三个指针对数组进行遍历,因为数组是从小到大排序后的,所以如果i遍历的第一个元素大于0,那么说明不必再继续向后遍历,因为如果第一个元素就大于0 ,那么三个元素加起来不可能为0。

        接下来,需要对这三个元素进行去重,也就是说一个满足三个数加和为0条件的元组里面的三个数可以相等,但是这三个数的排列组合不可以重复出现,所以需要在有一个元组满足条件后,对其他的元组进行去重处理。i、left、right三个都需要进行去重,一旦有一个满足条件的数组被存放进结果集,那么接下来再遇到这个数字,由于数组按大小排序,那么将它跳过即可。

        同时如果三个数的加和大于0,那么需要将右指针左移;如果加和小于0,那么左指针右移;如果加和等于0,那么将这个元组通过append()函数加入结果集。

(2)具体做法

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()
        
        for i in range(len(nums)):
            if nums[i] > 0:
                return result
        # i 作为元组第一个元素不可以大于0,因为数组按从小到大排列,否则三个数加和不可能等于0

            if i > 0 and nums[i] == nums[i-1]:
                continue
        # 由于对nums[i]进行剃重,所以需要用到num[i-1],因此i必须大于0

            left = i+1
            right = len(nums) - 1
        对指针left, right进行初始化

            while left < right:
            # 左指针不可以等于右指针,因为两个指针相遇,那么遍历的两个元素就合为一个了
                sum = nums[i] + nums[left] + nums[right]

                if sum > 0:
                    right -= 1
                elif sum < 0:
                    left += 1
                else:
                    result.append([nums[i],nums[left],nums[right]])
                # 满足三数之和等于0的条件,所以将这个元组放入结果集

                    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 result            

2. 实现过程中遇到的困难及解决

(1)需要对数组进行排序,使用到了sort()函数;

(2)关于如何进行去重处理:

        要做的是 不能有重复的三元组,但三元组内的元素是可以重复的!在对元素进行去重时,注意如果使用nums[i]==nums[i+1]然后continue的话,是将(-1,-1,2)这样的数组pass掉了,因为这种做法是排除元组内含有相等的元素,而不是排除重复的三元组,所以此处应该使用nums[i]==nums[i-1]来进行去重处理。

四、18. 四数之和

建议: 要比较一下,本题和 454.四数相加II 的区别,为什么 454.四数相加II 会简单很多,这个想明白了,对本题理解就深刻了。 本题 思路整体和 三数之和一样的,都是双指针,但写的时候 有很多小细节,需要注意,建议先看视频。

题目链接/文章讲解/视频讲解:代码随想录

1. 看到这道题目的第一想法

        未AC的代码:

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

        left = 0
        right = len(nums)-1

        while left+1 < right-1:
            sum = nums[left]+nums[left+1]+nums[right]+nums[right-1]
            if sum < target:
                left += 1
            elif sum > target:
                right -= 1
            else:
                result.append([nums[left],nums[left+1],nums[right],nums[right-1]])
                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 result

        首先定义空集合result用于收集结果集,然后对数组同样进行sort函数排序;接下来定义左指针和右指针代表a和d,b用a+1表示,c用d-1表示,然后计算四数之和,并判断sum与target的大小关系,如果小则左指针右移一位;如果大则右指针左移一位;如果等于则添加进入结果集,移动过程中保持b的指针和c的指针始终是小于关系;最后返回结果集。

2. 看完代码随想录的想法

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

        for i in range(len(nums)):
            if target > 0 and nums[i] > 0 and nums[i]>target:
                break
            if i > 0 and nums[i]==nums[i-1]:
                continue
            for k in range(i+1,len(nums)):
                if target > 0 and nums[i]+nums[k]>target:
                    break
                if k>i+1 and nums[k]==nums[k-1]:
                    continue
                left = k + 1
                right = len(nums)-1
                while left < right:
                    sum = nums[i]+nums[k]+nums[left]+nums[right]
                    if sum < target:
                       left += 1
                    elif sum > target:
                        right -= 1
                    else:
                        result.append([nums[i],nums[k],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 result

        需要注意的是没有说四个数两两必须相连,所以上述自己的做法错误。本题需要在三数之和的基础上在最外层再嵌套一层for循环,也就是在三数之和的i, left,right额外加一个k,并且k=i+1,left = k+1,right = len(nums)-1;其次本题的剪枝策略值得学习,在第一层需要限制target>0和nums[i]>0,才可以让nums[i]>target直接排除这种元组,实现剪枝;并且本题的第二层k循环的起点注意是i+1。 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值