代码随想录算法训练营day07| 第454题.四数相加II383. 赎金信第15题. 三数之和第18题. 四数之和

第454题.四数相加II

力扣题目链接

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

这段代码实现了一个名为`fourSumCount`的函数,用于计算四个数组中的元素相加等于零的组合数。

这里使用了一个哈希表`hashtable`,首先遍历`nums1`和`nums2`的所有组合,将它们的和作为键存储在哈希表中,如果和已经存在,增加对应键的计数,否则创建一个新的键。

然后,再次遍历`nums3`和`nums4`的组合,对于每对组合,检查它们的相反数(`-n3-n4`)是否存在于`hashtable`中,如果存在,将哈希表中该键的计数加到`count`中,以计算满足条件的组合数。

这个算法的时间复杂度为O(n^2),其中n是数组的长度。它通过使用哈希表来存储中间结果,避免了部分冗余的计算,提高了效率。

383. 赎金信

力扣题目链接

class Solution:
    def canConstruct(self, ransomNote: str, magazine: str) -> bool:
        if len(ransomNote) > len(magazine):
            return False
        return not collections.Counter(ransomNote) - collections.Counter(magazine)

你提供的代码是一个Python类Solution,其中包含一个名为canConstruct的方法,用于判断是否能够构造ransomNote字符串。它使用了Python中的collections.Counter来进行计数操作,并通过比较两个计数器的差异来判断是否能够构造ransomNote。

下面是代码的解释:

1. `if len(ransomNote) > len(magazine):`:首先,它检查ransomNote的长度是否大于magazine的长度,如果是,直接返回False,因为ransomNote无法通过magazine构造。

2. `return not collections.Counter(ransomNote) - collections.Counter(magazine)`:这一行代码是核心部分。它通过collections.Counter分别对ransomNote和magazine进行计数,然后使用 `-` 操作符计算两个计数器的差异。如果结果为空计数器,说明ransomNote中的每个字符都可以在magazine中找到,因此返回True,表示可以构造。否则,返回False,表示无法构造。

需要注意的是,代码中使用了collections.Counter,因此需要导入collections模块,可以在代码的开头添加 `import collections` 来导入该模块。

这个方法是一种巧妙的方式来解决构造问题,它不需要显式地构建字符串,而是通过字符计数的方式来判断。

第15题. 三数之和

力扣题目链接

class Solution:
    def threeSum(self, nums: [int]) -> [[int]]:
        nums.sort()
        res, k = [], 0
        for k in range(len(nums) - 2):
            if nums[k] > 0: break # 1. because of j > i > k.
            if k > 0 and nums[k] == nums[k - 1]: continue # 2. skip the same `nums[k]`.
            i, j = k + 1, len(nums) - 1
            while i < j: # 3. double pointer
                s = nums[k] + nums[i] + nums[j]
                if s < 0:
                    i += 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                elif s > 0:
                    j -= 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                else:
                    res.append([nums[k], nums[i], nums[j]])
                    i += 1
                    j -= 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
        return res

 

这段代码是用于找出一个整数数组中所有满足三数之和等于零的不重复三元组的算法。以下是其解释:

1. 首先,对输入的整数数组进行排序,这有助于后续的处理。

2. 创建一个空列表`res`用于存储找到的满足条件的三元组。

3. 使用变量`k`初始化为0,用于迭代数组的索引。

4. 开始循环遍历整数数组,直到倒数第三个元素为止(因为后面没有足够的元素可以组成三元组了)。

5. 在循环中进行以下处理:
   - 如果当前元素`nums[k]`大于零,直接退出循环,因为后面的元素肯定也大于零,不可能组成和为零的三元组。
   - 如果`k`大于零且当前元素与前一个元素相同,跳过当前循环,以避免重复计算相同的三元组。
   - 初始化两个指针`i`和`j`,分别指向`k`的下一个位置和数组的最后一个位置。
   - 进入内部循环,使用双指针法来查找满足条件的三元组。
   - 计算当前三个元素的和`s`。
   - 如果`s`小于零,说明需要增加和,将`i`指针向右移动,并跳过重复的元素。
   - 如果`s`大于零,说明需要减小和,将`j`指针向左移动,并跳过重复的元素。
   - 如果`s`等于零,说明找到了一个满足条件的三元组,将这个三元组添加到`res`列表中,然后分别移动`i`和`j`指针,同时跳过重复元素。
   
6. 循环结束后,返回包含所有满足条件的三元组的`res`列表。

这段代码的核心思想是使用排序和双指针法,在遍历数组的同时,根据元素之和与零的关系来不断调整指针的位置,以找到满足条件的三元组,同时避免了重复计算和重复的三元组。

第18题. 四数之和

class Solution(object):
    def fourSum(self, nums, target):

这是一个名为Solution的类,其中定义了一个名为fourSum的方法,该方法接受两个参数:nums是一个整数列表,target是目标和的值。

        freq = {}
        for num in nums:
            freq[num] = freq.get(num, 0) + 1

在方法内部,首先创建了一个空字典freq,用于存储数字和它们的频率(出现次数)。然后,遍历输入的整数列表nums,对字典freq进行初始化或更新。这个循环会计算每个数字在nums中出现的次数,并将其存储在freq字典中。

        ans = set()

接下来,创建一个空集合ans,用于存储最终的答案。集合的特点是其中的元素不会重复,这对于避免重复的答案非常有用。

        for i in range(len(nums)):
            for j in range(i + 1, len(nums)):
                for k in range(j + 1, len(nums)):

在这里,使用三层嵌套循环来遍历所有可能的组合。外层循环i从0到len(nums)-1,表示第一个数字的索引。第二层循环ji+1len(nums)-1,表示第二个数字的索引。第三层循环kj+1len(nums)-1,表示第三个数字的索引。

                    val = target - (nums[i] + nums[j] + nums[k])

在循环内部,计算目标值val,即目标和减去前三个数字的和。这里使用target - (nums[i] + nums[j] + nums[k])来计算。

                    if val in freq:

接下来,检查val是否存在于freq字典中,这意味着是否存在一个数字可以与前三个数字的和等于目标值。

                        count = (nums[i] == val) + (nums[j] == val) + (nums[k] == val)

如果val存在于freq字典中,进一步计算count,表示在前三个数字中有多少个等于val的数字。这里使用了三个条件表达式,分别检查nums[i]nums[j]nums[k]是否等于val,然后将结果相加。

                        if freq[val] > count:

然后,检查freq[val],即val的频率(出现次数),是否大于count。这是为了确保没有重复使用同一个数字。

                            ans.add(tuple(sorted([nums[i], nums[j], nums[k], val])))

如果满足上述条件,表示找到了一个满足条件的四元组,将这四个数字按升序排序,然后将它们作为元组添加到ans集合中。

        return [list(x) for x in ans]

最后,将ans集合中的所有元组转换为列表,并作为函数的返回值。这个列表包含了所有满足条件的四元组。

总体来说,这段代码使用嵌套循环遍历所有可能的组合,通过字典记录数字的频率,使用集合来避免重复,以寻找满足条件的四个数字的组合。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值