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

四数相加

题目链接:leetcode

题目描述:给你四个整数数组 nums1、nums2、nums3 和 nums4 ,数组长度都是 n ,请你计算有多少个元组 (i, j, k, l) 能满足:

  • 0 <= i, j, k, l < n
  • nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0

受两数之和的启发, 我们可以思考如何运用哈希表来解决加和问题。首先我们需要把加和分为两部分,一种是1+3,另一种是2+2. 由于1+3的遍历时间复杂度达到 O ( n 3 ) O(n^3) O(n3).因此我们考虑2+2. 将AB数组划分为一部分,CD为另一个。这里需要满足加和条件的频数,而不是下标,那就可以用字典来表示。其中key为两数之和的可能值,value则为两数之和出现的频数。
若dic1 表示AB形成的字典,遍历dic1,我们在dic2中找其相反数。若存在,count加上两数分别在两个字典的频数之积,最后返回count即可。

class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        n = len(nums1)
        dic1 = {}
        for i in range(n):
            for j in range(n):
                if nums1[i] + nums2[j] in dic1:
                    dic1[nums1[i]+nums2[j]] += 1
                else:
                    dic1[nums1[i]+nums2[j]] = 1
        dic2 = {}
        for i in range(n):
            for j in range(n):
                if nums3[i] + nums4[j] in dic2:
                    dic2[nums3[i]+nums4[j]] += 1
                else:
                    dic2[nums3[i]+nums4[j]] = 1

        count = 0
        for i in dic1:
            if -i in dic2:
                count += dic1[i] * dic2[-i]
        return count

赎金信

题目链接leetcode 383.

题目描述:给你两个字符串:ransomNote 和 magazine ,判断 ransomNote 能不能由 magazine 里面的字符构成。
如果可以,返回 true ;否则返回 false 。
magazine 中的每个字符只能在 ransomNote 中使用一次。

类似于有效的字母异位词,无序且关心频数。可以考虑哈希表储存每个字母的频数。这里对magazine的字母进行统计,然后我们没有必要对ransomNote 也进行统计,只需遍历ransomNote,在magazine中对应的字母频数减一,若出现负数,则返回false。不对ransomNote进行统计是因为可能不需遍历完ransomNote即可知道结果。
哈希表的实现可以是数组,也可以是字典。数组的底层实现可能会快些。这里用字典来实现,需要注意的是当value为0时,需要将其key删除。

Python

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
       
        dicMag = {}
        for i in magazine:
            if i in dicMag:
                dicMag[i] += 1
            else:
                dicMag[i] = 1
        
        for i in ransomNote:
            if i not in dicMag:
                return False
            else:
                dicMag[i] -= 1
                if dicMag[i] == 0:
                    del dicMag[i]       
        return True

三数之和

题目链接 leetcode 15

题目描述:给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。

首先考虑暴力解法。 因为题目要求不能有重复的三元组,而这个三元组的自由度为2,也就是说元组的其中两位相等即判为重复。如果我们对元组排了序,就只需要比较前两位。基于这个思想,我们来简单了解暴力解法。
首先对序列进行排序,然后三个嵌套循环 i , j , k , i < j < k ≤ n i,j,k, i< j <k \le n i,j,k,i<j<kn.
去重的操作:对于 i , j , k i,j,k i,j,k来说,如果与前一位一致,则跳过。特别的,如果找到了一个满足条件的三元组,那么 k k k就不需要继续遍历了,直接 j j j 遍历下一个。显然暴力解法时间复杂度为 O ( n 3 ) O(n^3) O(n3).

接下来考虑双指针解法。一样需要对序列先排序。
i i i 是一样的遍历和去重的操作。然后是定义左右指针 l = i + 1 , r = n − 1 l = i+1,r= n-1 l=i+1,r=n1。 双指针的思想是,比较此时三数和与目标值,如果大于目标值,则通过左移右指针来减少;如果小于目标值,则通过右移左指针来增大;若三数和恰为目标值,那么储存这三位数,并且同时更新左右指针(注意去重操作)。

当然,我们也要思考这方法是否有遗漏(尚未参透严谨的证明)。与暴力解法进行比较,第一层循环一样,我们比较 j , k j,k j,k l , r l,r l,r。最初 l , r l,r l,r 在数组两端,此时如是需要增大,即右移, 则只能 l l l 右移,同时说明在这个 i i i 下, l l l 在最左端,无论 r r r 在哪个位置都不满足条件,相当于遍历了所有 r r r,在给定此时 i , l i,l i,l的情况下。 若是需要左移也是同理。 此时 l , r l,r l,r 位置
[ i , − , l , … , r ] [i,-,l,\dots,r] [i,,l,,r]
这时若需左移,显然只能 r r r 左移( l l l左移已被遍历过)。右移也只能 l l l.然后 l , r l,r l,r相对位置
[ i , − , − , l … , r ] o r [ i , − , l , … , r , − ] [i,-,-,l\dots,r] \quad or\quad [i,-,l,\dots,r,-] [i,,,l,r]or[i,,l,,r,]
然后同理,一样只能 l l l右移, r r r左移

Python 代码实现

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

四数之和

题目链接:leetcode18

题目描述:给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] (若两个四元组元素一一对应,则认为两个四元组重复):

  • 0 <= a, b, c, d < n
  • a、b、c 和 d 互不相同
  • nums[a] + nums[b] + nums[c] + nums[d] == target
    你可以按 任意顺序 返回答案

题解与三数之和类似,只是在外面多套了一个循环。同时目标值给位任意给定 target 值而不是0. 这就使得预剪枝判定发生变化。nums[i] >0 改为 nums[i] > target and (nums[i] >= 0 or target >=0).

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

        for i in range(n):
            if nums[i] > target and (nums[i] >= 0 or target >= 0):
                break
            if i > 0 and nums[i] == nums[i-1]:
                continue
            for j in range(i+1,n):
                if j > i +1 and nums[j] == nums[j-1]: 
                    continue
                l,r = j+1,n-1
                while l < r:
                    sum = nums[i] + nums[j] + nums[l] + nums[r]
                    if sum > target:
                        r -= 1
                    elif sum < target:
                        l += 1
                    else:
                        result.append((nums[i],nums[j],nums[l],nums[r]))
                        while l < r and nums[l] == nums[l+1]: l += 1
                        while l < r and nums[r] == nums[r-1]: r -= 1
                        l,r = l+1,r-1
        return result
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值