【leetcode刷刷】454.四数相加II、383. 赎金信、15. 三数之和、18. 四数之和

文章讲述了如何使用双指针和去重技巧解决四数之和问题,涉及空间复杂度、时间复杂度分析,以及如何利用Python的Counter和排序功能优化解决方案。

454. 四数相加II

  1. # 两两处理,先统计nums1+nums2,再遍历nums3和nums4
    # 空间复杂度O(n2),时间复杂度O(n2)--->也是空间换时间了,不然暴力要O(n4)
    # 只需要次数,因此统计次数就可以了
    
class Solution:
    def fourSumCount(self, nums1: List[int], nums2: List[int], nums3: List[int], nums4: List[int]) -> int:
        add_count = dict()
        n = len(nums1)
        for i in range(n):
            for j in range(n):
                tmp = nums1[i]+nums2[j]
                add_count[tmp] = add_count.get(tmp, 0) + 1
        count0 = 0
        for i in range(n):
            for j in range(n):
                tmp = 0 - nums3[i] - nums4[j]
                count0 += add_count.get(tmp, 0)
        return count0

383. 赎金信

一些简便写法:

  1. 一句话py:return not Counter(ransomNote) - Counter(magazine)
  2. 一句话py:return all(ransomNote.count© <= magazine.count© for c in set(ransomNote))
  3. tip:dict需要维护红黑树或哈希表,所以纯字母计数的情况可能用数组[0]*26会比较快。
  4. 但是py里面的Counter也是专门为字母计数情况写的,不知道是不是区分大小写
  5. 此类题目要注意是否区分大小写,能否重复使用
class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        # ransomNote里的字母次数均小于magazine里的
        count1 = Counter(ransomNote)
        count2 = Counter(magazine)
        for i in count1:
            if count1.get(i) > count2.get(i, 0):
                return False
        return True

15. 三数之和

  1. 去重的细节好多啊,感觉不好想。
  • nums[i]和nums[i-1]进行比较,这是因为在对i-1进行双指针遍历的时候,就已经做完了nums[i]所有情况。
  • 在找到一个三元组之后,对left和right进行去重。重复的直接跳过。这个倒是比较直观。
  • 找到一个三元组后,left++的同时right–,这是因为如果只动一个但仍满足条件的话,说明是重复的。
  • 其实没有想过是不是能证明这样子做就一定不会出现重复
  1. 双指针的做法建立在排序的基础上。i遍历数组,是固定的,left和right两个指针进行移动。时间复杂度O(n2)。
  2. 本来想着用set()去重,但是三元组list不能作为key,不知道可不可以转换成str后进行去重。可能会多费一点空间,需要一个str对应三元组的dict。
  3. 双指针在两数之和可以使用的理由是,不需要下标,如果需要下标就不行了。或者可以建立一个dict进行查找?但是数是有重复的,所以还是不行。
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 不可以重复,去重,将三元组按从小到大的顺序排,再用set?
        # 双指针
        n = len(nums)
        output = []
        nums.sort()
        for i in range(n-2):  # 0 - n-3
            left = i+1
            right = n-1
            if i > 0 and nums[i] == nums[i-1]:   # ?为啥这里能跳过呢
                continue

            while(left < right):
                sum = nums[i] + nums[left] + nums[right]
                if sum < 0:
                    left += 1
                elif sum > 0:
                    right -= 1
                else:
                    output.append([nums[i], nums[left], nums[right]])
                    # 这边的去重逻辑还不是很懂
                    while right > left and nums[right] == nums[right-1]:
                        right -= 1
                    while right > left and nums[left] == nums[left+1]:
                        left += 1
                    left += 1
                    right -= 1
        return output

代码随想录的解答:这个没怎么看懂怎么去重的。

  • 元素b去重为什么需要比较两个??
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. 四数之和

  1. 和三数之和一个思路:双指针+去重。去重要注意每个数字都要去重,在哪里去重以及怎么去重需要考虑
  2. 字典法:list不能作为dict的key,但是tuple可以,因此可以把list先排序后转为tuple,再用集合set来实现去重,最后输出的时候要把tuple转回list。
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        n = len(nums)
        output = []
        nums.sort()  # 排序
        for i in range(n-3):
            # 去重
            if i > 0 and nums[i] == nums[i-1]:
                continue

            for j in reversed(range(i+1, n)):
                # 去重
                if j < n-1 and nums[j] == nums[j+1]:
                    continue

                left = i + 1
                right = j - 1
                while(left < right):
                    sum_ = nums[i] + nums[j] + nums[left] + nums[right]
                    if sum_ == target:
                        output.append([nums[i], nums[left], nums[right], nums[j]])

                        # 去重
                        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
                    elif sum_ > target:
                        right -= 1
                    else:
                        left += 1
        return output

字典解法:

class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        # 字典解法:
        freq = {}
        n = len(nums)
        for num in nums:
            freq[num] = freq.get(num, 0) + 1   # 计算频率
        ans = set()
        for i in range(n):
            for j in range(i+1, n):
                for k in range(j+1, n):
                    val = target - (nums[i] + nums[j] + nums[k])
                    if val in freq:
                        count = (nums[i] == val) + (nums[j] == val) + (nums[k] == val)
                        if freq[val] > count:
                            ans.add(tuple(sorted([nums[i], nums[j], nums[k], val])))
                            # 用tuple就可以作为key了??
        return [list(x) for x in ans]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值