(补)代码随想录算法训练有营DAY7|Leetcode 454、383、15、18

文章链接:代码随想录

454.四数相加II  + 383. 赎金信 + 15. 三数之和 + 18. 四数之和

视频链接:学透哈希表,map使用有技巧!LeetCode:454.四数相加II_哔哩哔哩_bilibili

哈希表第二部分,进一步体会哈希表的适用场景,双指针的使用、以及在三数之和、四数之和的去重和剪纸问题。

454.四数相加II 

状态:在学过leetcode 1 两数之和的基础上,对这道题有一定的思路:考虑到了先计算前两个数组的a+b,再从后两个数组中找符合条件的元素;看视频确认思路是否正确,再自己写代码

思路:用dict()记录前两个数组的a+b之和及其出现的次数;遍历后两个数组,从dict()中找到等于0-(c+d)的key,并将相应的value(出现次数)加到count中

总结:

1. 思路比较清晰,但最开始没有想清楚怎样计算元组个数的问题, 题目不要求去重,所以直接统计dict()中key对应的value

2. 遍历前两个数组,dict()初始化为空字典 {} ,进行dict()的键值对更新时,用的是if判断语句:如果key存在,则更新sum[key] += 1; 否则,添加键值对sum[key] = 1

3. 时间复杂度 O(n^2), 空间复杂度 O(n^2)

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

        for m in nums3:
            for n in nums4:
                target = 0 - (m + n)
                if target in sums1:
                    count += sums1[target]
        return count

383. 赎金信

状态:结合有效的异位词的解法,自己写代码AC, 多数时间耗在思考最后的if 判断语句该怎么写

思路:题目提示只考虑小写字母,所以初始化一个长为26的全0数组count;首先统计ransomNote 中各字母出现的次数,在统计好的count上做减法(magazine每出现一次某字母,count的对应元素就减1);最后遍历ransomNote的所有元素,若存在某字母的count个数大于0,则返回False

总结:

1. 最后的if 判断语句的逻辑:数组count是按照ransomnote的字母出现情况计数的,与magazine的字母出现情况做减法后,若存在count[xx] > 0的情况,则说明不能从magazine中找到足够多的字母xx来构成ransomnote

2. 看过示例代码,学习到了可以用python collections模块的函数Counter 实现更快速简洁的解法

3. 时间复杂度 O(n), 空间复杂度 O(1)

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

15. 三数之和

状态:先看视频,再自己尝试写代码,但对于去重的细节问题还没有完全掌握

思路:

一个for循环+双指针,分别代表三个数

先对数组排序,用一个for循环进行第一个数nums[i]从下标0开始的遍历,指针left从 i+1 开始,指针right从数组末尾 len(nums)-1开始;找nums[i] + nums[left] + nums[right] = 0的元组

移动双指针:(前提是升序的数组!!)当sums>0时,可将指针right向左移;当sums<0时,可将指针left向右移;若sums=0, 则保存该元组[nums[i ], nums[left], nums[right]]

去重:

       1) nums[i]去重:if i > 0 and sorted_list[i] == sorted_list[i - 1]

        2)nums[left]去重:while left < right and sorted_list[left] == sorted_list[left + 1]

        3)nums[right]去重:while left < right and sorted_list[right] == sorted_list[right - 1]

        注:left 和 right 的去重逻辑必须放在收取一个结果之后,比如考虑特殊情况:全0数组,此时的[0,0,0]是符合条件,但如果将left 和 right 的去重放在while循环最开始,[0,0,0]这样的数组就无法被获取

总结:

1. 首先必须将数组进行排序

2. 去重的逻辑一定要搞清楚,尤其是left 和 right 的去重,必须放在收取一个结果之后,且要用while而不是if判断

3. 去重判断结束后,还要对双指针进行移动操作

4. 时间复杂度O(n^2),空间复杂度O(1)

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        result = []
        sorted_list = sorted(nums)
        for i in range(len(nums)):
            if sorted_list[i] > 0:
                return result
            if i > 0 and sorted_list[i] == sorted_list[i - 1]:
                continue
            left = i + 1
            right = len(nums) - 1
            while left < right:
                sums = sorted_list[i] + sorted_list[left] + sorted_list[right]
                if sums > 0:
                    right -= 1
                elif sums < 0:
                    left += 1
                else:
                    result.append([sorted_list[i], sorted_list[left], sorted_list[right]])
                    while left < right and sorted_list[right] == sorted_list[right - 1]:
                        right -= 1
                    while left < right and sorted_list[left] == sorted_list[left + 1]:
                        left += 1
                    left += 1
                    right -= 1
        return result
                 

18. 四数之和

状态:先看视频,再自己尝试写代码,还是细节问题:二级去重

思路:与三数之和类似,但多一层for循环,且剪枝和去重的细节问题更多

总结:

1. 剪枝过程:

        一级剪枝 if nums[i] > 0 and target > 0 and nums[i] > target

        二级剪枝if nums[i] + nums[j] > 0 and target > 0 and nums[i] + nums[j] > target

2. 二级去重时,j的起始点应为if j > i+1 and nums[j] == nums[j - 1]

3. 以此类推,五数之和、六数之和也可以用相似的思路解决

4. 看示例代码,还可以用dict():首先创建一个dict()来存储nums中每个数的频率,再创建一个set()来存储最终答案,并遍历4个数字的所有唯一组合。其中,对数字的重复性判断很有意思

5. 时间复杂度O(n^3),空间复杂度O(1)

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        result = []
        for i in range(len(nums)):
            if nums[i] > 0 and target > 0 and nums[i] > target:
                break
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i+1, len(nums)):
                if nums[i] + nums[j] > 0 and target > 0 and nums[i] + nums[j] > target:
                    break
                if j > i+1 and nums[j] == nums[j - 1]:
                    continue
                left = j + 1
                right = len(nums) - 1
                while left < right:
                    sum_ = nums[i] + nums[j] + nums[left] + nums[right]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值