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

LeetCode 454.四数相加Ⅱ:

文章链接
题目链接:454.四数相加

看到题目后的思路:

可以算作之前两数之和的扩展。具体思路是,
① 首先遍历其中两个数组A和B,将两个元素的和与和出现的次数计入哈希表中,其中key为元素和,value为次数。
② 遍历剩下两个数组C和D,判断0-C[i]-D[j]是否在哈希表中,在的话当前的计数count + value。
③ 因为题目中给出的是四个数组,在四个数组中各找一个元素满足条件,因此不需要去除重复的元素(比如A[i] + B[j] = 1 + 2 , A[k] + B[l] = 2 + 1)

# 使用dict字典和dict.get
# dict.get(key, value)根据key获取对应的值,不存在则返回默认值value
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
        """
        hashmap = dict()
        # 创建哈希表
        for num1 in nums1:
            for num2 in nums2:
                hashmap[num1 + num2] = hashmap.get(num1+num2, 0) + 1
        count = 0   # 计数
        for num3 in nums3:
            for num4 in nums4:
                value = hashmap.get(0 - num3 - num4, 0)
                count += value
        return count

LeetCode 383.赎金信:

文章链接
题目链接:383.赎金信

看到题目后的思路:

  1. 哈希表
    是字母异构体的拓展,并且字符串也只使用小写字母,思路相似:
    ① 使用数组构造magazine的哈希表,下标对应字母,值为字母出现的次数
    ② 遍历randomNote,哈希表中每个字母对应的位置的值 - 1
    ③ 如果最后哈希表中出现负数,表明randomNote不能由magazine里面的字符构成
class Solution(object):
    def canConstruct(self, ransomNote, magazine):
        """
        :type ransomNote: str
        :type magazine: str
        :rtype: bool
        """
        record = [0] * 26
        for s in magazine:
            record[ord(s) - ord('a')] += 1
        for r in ransomNote:
            record[ord(r) - ord('a')] -= 1
        for r in record:
            if r < 0:
                return False
        return True
  1. 暴力
    双重循环遍历ran和maga,当m在ran中出现时,ran中移除对应的元素
        ran = list(ransomNote)
        maga = list(magazine)
        # 暴力
        for m in maga:
            for r in ran:
                if m == r:
                    ran.remove(r)
                    break
        if len(ran) == 0:
            return True
        return False

LeetCode 15.三数之和 :

文章链接
题目链接:15.三数之和

看到题目后的思路:

  1. 双指针
    因为题目需要返回的是数值而不是下标,所以可以采用先排序数组再使用双指针的方法实现
    ① 本题中双指针的主要思路是,首先使用 i 遍历数组中的元素,双指针left = i + 1,right = len(num) - 1;若[left] + [right] <(>) 0 - [i],left向后移(right向前移);若[left] + [right] == 0 - [i],将该三元组加入答案中
    ② 题目需要返回的是不重复的三元组,所以我们需要做去重的工作。这个工作可以分为两部分:对 i 的去重和对双指针的去重。
    对 i 的去重:
[i] == [i - 1]  continue
不能采用 [i] == [i + 1] continue,因为对于[0,0,0]输入数组来说,应当返回的三元组就是[0,0,0],如果采用[i] == [i + 1],最终返回NULL(因为 left 是从i + 1开始的)

③ 对双指针的去重:先找到符合条件的三元组,left和right再进行去重

class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        len_num = len(nums)
        for i in range(len_num):
            if nums[i] > 0: # 第一个元素大于0,后面一定不存在符合条件的三元组
                break   
            if i > 0 and nums[i] == nums[i - 1]:    # 对i的去重
                continue
            left, right = i + 1, len_num - 1
            while left < right: 
                cursum = nums[i] + nums[left] + nums[right]
                if cursum < 0:
                    left += 1
                elif cursum > 0:
                    right -= 1
                else:
                    result.append([nums[i], nums[left], nums[right]])
                    # 跳过相同的元素避免重复
                    while left < right and nums[right] == nums[right - 1]:
                        right -= 1
                    while left < right and nums[left] == nums[left + 1]:
                        left += 1
                    # 退出循环时,[right] = [right + 1], [left] = [left - 1]
                    right -= 1
                    left += 1
        return result
  1. 哈希
    思想与之前的求两个元素的和使用哈希表的思想相似,但是这里是先确定一个元素,在剩下的元素中使用哈希表求符合条件的元组。
    去重比较复杂
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        len_nums = len(nums)
        for i in range(len_nums):
            if nums[i] > 0: # 第一个元素大于0,后面不可能有符合条件的三元组
                break
            if i > 0 and nums[i] == nums[i - 1]:    # 跳过重复的元素
                continue
            #print('i' + str(i) + ': ' + str(nums[i]))
            hash_dict = {}
            # 哈希表法
            for j in range(i + 1, len_nums):
            	# 对于类似[0,0,0,0]的情况
                if j > i + 2 and nums[j] == nums[j - 1] == nums[j - 2]:
                    continue
                c = 0 - nums[i] - nums[j]
                #print('j' + str(j) + ': ' + str(nums[j]))
                #print('hash_dict')
                #self.print_list(hash_dict)
                if c in hash_dict:
                    result.append([nums[i], nums[j], c])
                    # 对于类似[-2,0,0,2,2]的情况
                    hash_dict.pop(c)
                else:
                    hash_dict[nums[j]] = j
        return result
    def print_list(self, plist):
        for key in plist:
            print(key)
        print('\n')

实现过程中遇到的困难:

使用哈希表的去重比较复杂,基本是遇到错误,将错误实例的情况加上去。


LeetCode 18.四数之和:

文章链接
题目链接:18.四数之和

看到题目后的想法:

思路与三数之和相同,只是从一重循环确定第一个元素变为了二重循环确定第二个元素。重点在于剪枝
① 需要注意的是target是题目给定的,可能>0,可能<0,而排序后的数组按照从大到小排列,因此剪枝只能在target > 0的时候进行剪枝。
②确定第一个元素剪枝的部分是target > 0并且当前元素 > target。第二个元素剪枝的部分是target > 0并且当前两个元素的和 > target

class Solution(object):
    def fourSum(self, nums, target):
        """
        :type nums: List[int]
        :type target: int
        :rtype: List[List[int]]
        """
        result = []
        nums.sort()
        len_nums = len(nums)
        for i in range(len_nums):
            # 第一个元素a去重
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            # 剪枝。target > 0时可以剪枝
            if nums[i] > target and target > 0:
                break
            #print('i: ' + str(i) + ' ' + str(nums[i]))
            for j in range(i + 1, len_nums):
                # 第二个元素b去重
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                #print('j: ' + str(j) + ' ' + str(nums[j]))
                # target > 0时如果前两个元素的和超过target,后面一定不存在满足条件的四元组
                if nums[i] + nums[j] > target and target > 0:
                    break
                left, right = j + 1, len_nums - 1
                while left < right:
                    cursum = nums[i] + nums[j] + nums[left] + nums[right]
                    if cursum < target:
                        left += 1
                    elif cursum > target:
                        right -= 1
                    else:
                        #print('left: ' + str(left) + ' ' + str(nums[left]))
                        #print('right: ' + str(right) + ' ' + str(nums[right]))
                        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

实现过程中遇到的困难:

剪枝,最开始没有注意到target 可以小于0,所以按照三数之和进行求解。


学习收获:

加深了对哈希表的使用
学会了python中的dict.get方法
剪枝和去重

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值