代码随想录算法训练营第7天 |● 454.四数相加II ● 383. 赎金信 ● 15. 三数之和 ● 18. 四数之和


前言

链接都不放了,放一个总的吧以后 代码随想录文章

454.四数相加II

在这里插入图片描述
在这里插入图片描述

思路

总体思路:昨天有两数相加的方法,所以这里4个划分为两个为一组遍历,转换为两数相加的方法
注意事项:本题比下面第四题简单,因为重复的也算,所以A、B数组如果有k个组合加起来都为3,C、D数组一种组合为-3,那么对应这个C、D组合的个数就有k个,就得算;
结构选择:因为不仅仅要存储A、B是否有过这个数,还要存储A、B之和出现这个数的次数,采用map结构
在这里插入图片描述

在这里插入图片描述

方法一 常规python

我写的代码:知道思路之后很简单,没有上面要注意的

class Solution(object):
    def fourSumCount(self, nums1, nums2, nums3, nums4):
        """
        :type nums1: List[int]
        :type nums2: List[int]
        :type nums3: List[int]
        :type nums4: List[int]
        :rtype: int
        """
        count = 0 
        map_ = {}
        for a in nums1:
            for b in nums2:
                sum1 = a+b
                if sum1 in map_:
                    map_[sum1] += 1
                else:
                    map_[sum1] = 1
        
        for c in nums3:
            for d in nums4:
                target = -c-d
                if target in map_:
                    count += map_[target]
        return count

使用字典的get属性:dict1.get(key, defatult)返回key对应的值,如果没有返回defatult的值

class Solution(object):
    def fourSumCount(self, nums1, nums2, nums3, nums4):
        # 使用字典存储nums1和nums2中的元素及其和
        hashmap = dict()
        for n1 in nums1:
            for n2 in nums2:
                hashmap[n1+n2] = hashmap.get(n1+n2, 0) + 1#注意这一句的写法
        
        # 如果 -(n1+n2) 存在于nums3和nums4, 存入结果
        count = 0
        for n3 in nums3:
            for n4 in nums4:
                key = - n3 - n4
                if key in hashmap:
                    count += hashmap[key]
        return count

方法二 使用defaultdict

使用defaultdict就和c++的map差不多的使用方法了

from collections import defaultdict 
class Solution:
    def fourSumCount(self, nums1: list, nums2: list, nums3: list, nums4: list) -> int:
        rec, cnt = defaultdict(lambda : 0), 0
        for i in nums1:
            for j in nums2:
                rec[i+j] += 1
        for i in nums3:
            for j in nums4:
                cnt += rec.get(-(i+j), 0) 
        return cnt

383. 赎金信

在这里插入图片描述
在这里插入图片描述

思路

时间复杂度: O(n)
空间复杂度: O(1)

方法一: 哈希表

和前面一天的有效字母异位词一样的思路

自己直接写的长这样,很简单

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        hash_ = [0]*26
        #magazine code into hash table
        for i in magazine:
            hash_[ord(i)-ord('a')] += 1
        for i in ransomNote:
            hash_[ord(i)-ord('a')] -= 1
        for ele in hash_:
            if ele < 0:
                return False
        return True

使用defaultdict,思路和我一样

from collections import defaultdict

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:

        hashmap = defaultdict(int)

        for x in magazine:
            hashmap[x] += 1

        for x in ransomNote:
            value = hashmap.get(x)
            if not value or not value:
                return False
            else:
                hashmap[x] -= 1

        return True

方法二 使用count

使用count:保证每个ransomNote中的字母个数小于杂志上的字母个数,擅于使用count属性;

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        for char in ransomNote:
            if char in magazine and ransomNote.count(char) <= magazine.count(char):
                continue
            else:
                return False
        return True

15. 三数之和

在这里插入图片描述

这道题目很不简单;
题目分析

  1. 主要是去重;例如如果数组长成[-1,-1,0,0,0,1,1,1,1]的样子,可能会得到好几次[-1,0,1]的结果;所以需要去重
  2. 本题目中返回元素元组而不需要下标,所以可以先排序

方法一 双指针法

其实这道题目使用哈希法并不十分合适,因为在去重的操作中有很多细节需要注意,在面试中很难直接写出没有bug的代码。
总体思路:a从头遍历【下标为i】,后面两个b和c之和必须为-a;再用双指针法找满足条件的b和c
双指针法核心:b和c分别用left和right指针,和-a比较,大了就left后移动,小了就right前移动

具体步骤

  1. 首先将数组排序,然后有一层for循环,i从下标0的地方开始,同时定一个下标left 定义在i+1的位置上,定义下标right 在数组结尾的位置上。
  2. 移动left 和right呢, 如果nums[i] + nums[left] + nums[right] > 0 就说明 此时三数之和大了,因为数组是排序后了,所以right下标就应该向左移动,这样才能让三数之和小一些。如果 nums[i] + nums[left] + nums[right] < 0 说明 此时 三数之和小了,left 就向右移动,才能让三数之和大一些,直到left与right相遇为止。
  3. 小细节:如果排序之后第一个数值已经大于0了就return,一定不满足

时间复杂度: O(n^2)
空间复杂度: O(1)

🎄去重注意点: a, b ,c, 对应的就是 nums[i],nums[left],nums[right],三个部分都需要去重,分为a的去重与b&c的去重

  1. 遍历的i需要和i-1比较,如果开头的a出现过了,就不要了;记住是当i>0的时候,从第二个元素开始
  2. 后面的b+c也会重:例如[-1,0,0,0,1,1,1]
    b&c的去重方法:判断如果b和b+1相等,left就向中间移动,c和c-1相等,right就往中间移动;

明显需要注意:收获结果必须要放在去重的上面,否则出现[0,0,0,0]还没有记录结果呢,就没了

记一下carl的板书
在这里插入图片描述
自己写代码犯错

  1. list的sort只需要a.sort()就行了,它返回的是None,不要用a=a.sort()
  2. 如果第一个元素已经大于0,不需要进一步检查;后面的都不需要哦,直接return就行
  3. 去重b&c的时候:加上条件right > left;否则可能index会超出范围;并且必须使用while不能是if,否则b&c重复的就会去除不干净;
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        #排序,默认从小到大
        nums.sort()
        result = []
        for i in range(len(nums)-1):#-1要不要都行
            if nums[i] > 0:#如果a,b,c中第一个元素【最小】大于0的话,一定没有满足条件的
                return result
            if i >0 and nums[i] == nums[i-1]:#去除a和之前重合的情况
                continue
            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]])
                #去重b和c
                    while right > left and nums[right] == nums[right - 1]:
                        right -= 1
                    while right > left and nums[left] == nums[left + 1]:
                        left += 1
                        
                    right -= 1
                    left += 1
        return result

力扣网站耗尽了我今天所有的好脾气和耐心,一摸一样的代码,就是报错。

方法二 哈希法

我没看,有很多去重的小细节。力扣的网页bug浪费了今天太多时间了🌿

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        nums.sort()
        # 找出a + b + c = 0
        # a = nums[i], b = nums[j], c = -(a + b)
        for i in range(len(nums)):
            # 排序之后如果第一个元素已经大于零,那么不可能凑成三元组
            if nums[i] > 0:
                break
            if i > 0 and nums[i] == nums[i - 1]: #三元组元素a去重
                continue
            d = {}
            for j in range(i + 1, len(nums)):
                if j > i + 2 and nums[j] == nums[j-1] == nums[j-2]: # 三元组元素b去重
                    continue
                c = 0 - (nums[i] + nums[j])
                if c in d:
                    result.append([nums[i], nums[j], c])
                    d.pop(c) # 三元组元素c去重
                else:
                    d[nums[j]] = j
        return result

18. 四数之和

在这里插入图片描述

思路

总体思路:和三数之和思路一致,就是在外面又套了一层循环for k来处理多出来的元素
细节处理:去重和剪枝

  1. 一级剪枝:for k,在这里不能因为nums[k]>target就break了,因为有可能出现下面的情况nums=[-4,-1,0,0] target = -5; 如果是负数,两个负数相加会变小;所以for k 的剪枝要变成nums[i] > target && (nums[i] >=0 || target >= 0)
  2. 一级去重:如果**k>0**,并且和k-1对应元素值相同,那么需要continue
  3. 二级剪枝:i=k+1,思路和一级剪枝一样,就是要把nums[k]+nums[i]看成一个整体进行判断;
  4. 二级去重:如果**i>k+1**【背住这个;理解:因为k固定之后,出现两个一样的i才需要往后面移动】,并且和i-1元素相同,continue;

在这里插入图片描述

在这里插入图片描述

注意:二级剪枝的时候只能用break,退出这个i循环,k继续

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        for k in range(len(nums)):
            if nums[k]>target and nums[k]>0 :
                return result #一级剪枝
            if k>0 and nums[k] == nums[k-1]:#一级去重
                continue
            for i in range(k+1,len(nums)):
                if nums[k]+nums[i]>target and target > 0:#2级剪枝
                    # return result 
                    break
                if i > k+1 and nums[i] == nums[i-1]:
                    continue
                left = i+1
                right = len(nums)-1
                while left < right:
                    sum_ = nums[k]+nums[i] +nums[left]+nums[right]
                    if sum_ > target:
                        right -= 1
                    elif sum_ < target:
                        left += 1
                    else:
                        result.append([nums[k],nums[i],nums[left],nums[right]])
                    #去重b和c
                        while right > left and nums[right] == nums[right - 1]:
                            right -= 1
                        while right > left and nums[left] == nums[left + 1]:
                            left += 1
                            
                        right -= 1
                        left += 1
        return result

总结

没有时间慢慢调代码,时间真的很紧张;每天最多只能花3h;

  • 15
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值