代码随想录算法训练营第七天

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

454. 四数相加II

题目链接:四数相加II
先吐槽一下这个题nums4[l]我还以为是nums4一直取1,想了半天为什么4要存在。
因为这个题是四个nums,所以不用考虑查虫的问题,所以直接当三数之和的哈希版做就好了。
首先制作一个哈希计算nums1和nums2能有几种和,每种和有几个。
然后用nums3和nums4遍历,去找0-nums3-nums4在上面的哈希表里存不存在。
两个比较幼稚的注意点:

  1. 如果存在的话加上的是这个和之前在nums1和nums2里出现过的次数,并不是直接+1
  2. 找到次数用的key是hash[-nums3[i]-nums4[j]]而不是hash[nums3[i]+nums4[j]],找错误找了好久没发现……
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
        d=defaultdict(int)
        for i in nums1:
            for j in nums2:
                d[i+j] += 1
        
        for i in nums3:
            for j in nums4:
                if (0-i-j) in d:
                    count+=d[-i-j] 
        return count                    

383. 赎金信

题目链接:赎金信
这个题和昨天的哈希表都差不多,就是算一下每个字母出现了几次然后比较一下就好。

class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        d1=defaultdict(int)
        d2=defaultdict(int)
        for i in magazine:
            d1[i]+= 1
        for i in ransomNote:
            d2[i] += 1
        
        for i in d2:
            if i not in d1:
                return False
            elif d2[i] > d1[i]:
                return False
        return True

15. 三数之和

题目链接:三数之和

哈希

哈希的思路和二数之和是一样的:
例子:nums = [-1,0,1,2,-1,-4]
d = []用来存放遍历过的元素
一个for从nums[0]开始循环,一个for从nums[1]开始循环,然后在d里找是否存在(0-nums[0]-nums[1]),有的话就加到result里去,没有就在d里存一个nums[j],然后去下一个。

注意点:

  1. 有一个问题是当j从(i+1)到最后遍历完一次之后,d该怎么处理。我这里的做法是把d清空了然后只放已经遍历过得nums[i]进去,这样才能避免重复。
  2. 另一个问题是result的list里是要求不能有重复的,我的做法(可能是导致超时的原因)是每次放进去之前都排个序,这样重复的也可以找到。

自己写了一个在308 / 312的地方超时了。

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        d = []
        l = []
        for i in range(len(nums)):
            l.append(nums[i])
            for j in range((i+1),len(nums)):
                a = 0-(nums[i]+nums[j])
                if (a in d) and (sorted([nums[i],nums[j],a]) not in result):
                    result.append(sorted([nums[i],nums[j],a]))
                    d.append(nums[j])
            d=[]
            d = d + l
        return result

双指针

双指针真的非常的巧妙
例子:nums = [-1,0,1,2,-1,-4]
首先进行排序,nums = [-4,-1,-1,0,1,2]
我们现在可以只要一个for循环,从0到list最后。
首先i走到nums[0]不动,然后安排两个指针,left和right,left指向nums[1]右边指向最后一个元素。

  1. 如果nums[i]+nums[left]+nums[right] == 0,那么放进result
  2. 如果nums[i]+nums[left]+nums[right] < 0,那么把left往左移动来加大。
  3. 如果nums[i]+nums[left]+nums[right] > 0,那么把right往右移动来减小。

注意点:

  1. 首先就是在for循环遍历的时候,如果有前面已经遍历过得元素就跳过,lizi里的第二个-1就可以跳过,看例子,如果在重复一次还是会再得到一个[-1,0,1],而且浪费时间,我这里是做了一个储存动作,感觉这一步也比较浪费时间。
  2. 还有一个减枝动作是,因为是排序的,nums[i]应该是这个三元组最小的那个,如果他都已经大于0了那么后面也不用看了。
  3. while left<right的时候,如果找到了==0的情况,一定要记得出循环啊,忘记出循环的我陷入了死循环。
  4. 不排除在nums[i]之后的list里有很多重复的元素,但由于我们一开始就给数组排序了,重复就是连顺序都重复了,那就可以直接去check是不是if [nums[i],nums[left],nums[right]] in result

debug到崩溃的一道题,最后速度还是很慢也没有办法了QAQ

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        nums.sort()
        #print(nums)
        result = []
        l = []
        for i in range(len(nums)-2):
            if (nums[i] in l) or (nums[i]>0):
                continue
            else:
                left = i+1
                right = len(nums)-1
                while left < right:
                    if (nums[i]+nums[left]+nums[right] == 0) and ([nums[i],nums[left],nums[right]] not in result):
                        result.append([nums[i],nums[left],nums[right]])
                        left += 1
                        right -= 1
                    elif nums[i]+nums[left]+nums[right] < 0:
                        left += 1
                    else:
                        right -= 1
            l.append(nums[i])
        return result

一些无效剪枝

写完四数之和突然发现自己三数之和在做一些无效剪枝

if (nums[i] in l) or (nums[i]>0):
	continue

这个位置其实应该是在nums[i]>0的时候直接break掉的,最小的都大于target了后面就不用去想了。

if nums[i]>0:
	break
if nums[i] in l:
    continue

18. 四数之和

题目链接:四数之和
视频解析:四数之和
本来以为看了视频自己写也要干很久,但还行,推荐看视频解析,卡老师说的非常清楚。

双指针

这题和三数之和的做法是一样的,双指针怎么用还是看上一题,这题主要是说怎么剪枝和怎么去重。

  1. 关于去重,这里没有用第三题我自己那种浪费空间的做法,虽然不用动脑子,当感觉比较浪费,两个去重点if (i>0) and (nums[i] == nums[i-1]):if (j>i+1) and (nums[j] == nums[j-1]):最关键的地方在于i>0j>i+1其实一开始我没有想明白,觉得i>0j>i+1应该是天然成立的,写着没什么意义,但并不是,i=0j=i+1就是开始重复的地方,第一次得做完,不然就是去重到自己都没了。
  2. 关于剪枝,这里的剪枝的问题就是直接nums[i]>target是剪不掉的,因为如果大家都是负的,那负的加负的就是更负,那是可以达到target的,所以这里不能剪,要加上大家都是正的条件才可以剪掉。同样参考三数之和的无效剪枝,这里应该是break不是continue
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        #[-2,-1,0,0,1,2,3,4]
        result = []
        for i in range(len(nums)-3):
            if (nums[i]>0) and (target>0) and (nums[i]>target): #剪枝
                break #这里是可以直接break的因为最小的都大于target再往后也没什么意义了。
            if (i>0) and (nums[i] == nums[i-1]): #去重,j>0是因为j=0不用管
                continue
            for j in range((i+1),len(nums)-2):
                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:
                    s = nums[i]+nums[j]+nums[left]+nums[right]
                    if (s == target) and ([nums[i],nums[j],nums[left],nums[right]] not in result):
                        result.append([nums[i],nums[j],nums[left],nums[right]])
                        left += 1
                        right -= 1
                    elif s < target:
                        left += 1
                    else:
                        right -= 1
        return result
                



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值