18. 四数之和

Posted 炫云云

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了18. 四数之和相关的知识,希望对你有一定的参考价值。

18. 四数之和

1.两数之和

15. 三数之和

18. 四数之和

20、n数之和

给你一个由 n 个整数组成的数组 nums ,和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]

  • 0 <= a, b, c, d < n

  • abcd 互不相同

  • nums[a] + nums[b] + nums[c] + nums[d] == target

示例 1:

输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]

排序 + 双指针

使用双循环固定两个数,用双指针找另外两个数,通过比较与target 的大小,移动指针。

假设两重循环枚举到的前两个数分别位于下标 i i i j j j ,其中 i < j i<j i<j 。初始时,左右指针分别指向下标 j + 1 j+1 j+1 和下标 n − 1 n-1 n1。 每次计算四个数的和,并进行如下操作:

  • 如果和等于 target \\textit{target} target ,则将枚举到的四个数加到答案中, 执行left += 1right -= 1并跳过所有重复的nums[left]nums[right],防止记录到重复组合。
  • 如果和小于 target \\textit{target} target ,则left += 1
  • 如果和大于 target \\textit{target} target ,则right -= 1

具体实现时,还可以进行一些剪枝操作:

  • 在确定第一个数之后,如果 nums [ i ] + nums [ i + 1 ] + nums [ i + 2 ] + nums [ i + 3 ] > target \\textit{nums}[i]+\\textit{nums}[i+1]+\\textit{nums}[i+2]+\\textit{nums}[i+3]>\\textit{target} nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target​,说明此时剩下的三个数无论取什么值,四数之和一定大于 target \\textit{target} target​ ,因此退出第一重循环;
  • 在确定第一个数之后,如果 nums [ i ] + nums [ n − 3 ] + nums [ n − 2 ] + nums [ n − 1 ] < target \\textit{nums}[i]+\\textit{nums}[n-3]+\\textit{nums}[n-2]+\\textit{nums}[n-1]<\\textit{target} nums[i]+nums[n3]+nums[n2]+nums[n1]<target ,说明此时剩下的三个数无论取什么值,四数之和一定小于 target , \\textit{target} , target因此第一重循环直接进入下一轮,枚举 nums [ i + 1 ] \\textit{nums}[i+1] nums[i+1]​ ;
  • 在确定前两个数之后,如果 nums [ i ] + nums [ j ] + nums [ j + 1 ] + nums [ j + 2 ] > target \\textit{nums}[i]+\\textit{nums}[j]+\\textit{nums}[j+1]+\\textit{nums}[j+2]>\\textit{target} nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,说明此时剩下的两个数无论取什么值,四数之和一定大于 target \\textit{target} target​ ,因此退出第二重循环;
  • 在确定前两个数之后,如果 nums [ i ] + nums [ j ] + nums [ n − 2 ] + nums [ n − 1 ] < target \\textit{nums}[i]+\\textit{nums}[j]+\\textit{nums}[n-2]+\\textit{nums}[n-1]<\\textit{target} nums[i]+nums[j]+nums[n2]+nums[n1]<target ,说明此时剩下的两个数无论取什么值,四数之和一定小于 target \\textit{target} target ,因此第二重循环直接进入下一轮,枚举 nums [ j + 1 ] \\textit{nums}[j+1] nums[j+1]
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        quadruplets = list() # 定义一个返回值
        if not nums or len(nums) < 4:
            return quadruplets

        nums.sort()
        length = len(nums)
        # 定义4个指针i,j,left,right  i从0开始遍历,j从i+1开始遍历,留下left和right作为双指针
        for i in range(length - 3):
            if i > 0 and nums[i] == nums[i - 1]: # 当i的值与前面的值相等时忽略
                continue
            # 获取当前最小值,如果最小值比目标值大,说明后面越来越大的值根本没戏
            if nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target:
                break # 这里使用的break,直接退出此次循环,因为后面的数只会更大
            # 获取当前最大值,如果最大值比目标值小,说明后面越来越小的值根本没戏,忽略
            if nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target:
                continue # 这里使用continue,继续下一次循环,因为下一次循环有更大的数
            # 第二层循环j,初始值指向i+1
            for j in range(i + 1, length - 2):
                if j > i + 1 and nums[j] == nums[j - 1]: # 当j的值与前面的值相等时忽略
                    continue
                if nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target:
                    break
                if nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target:
                    continue
                left, right = j + 1, length - 1
                # 双指针遍历,如果等于目标值,left++并去重,right--并去重,
                # 当当前和大于目标值时right--,当当前和小于目标值时left++
                while left < right:
                    total = nums[i] + nums[j] + nums[left] + nums[right]
                    if total == target:
                        quadruplets.append([nums[i], nums[j], nums[left], nums[right]])
                        left += 1 # left先+1之后,和它前面的left-1进行比较,若后+1,则和它后面的left+1进行比较
                        right -= 1
                        while left < right and nums[left] == nums[left - 1]:
                            left += 1
                        while left < right and nums[right] == nums[right + 1]:
                            right -= 1   
                    elif total < target:
                        left += 1
                    else:
                        right -= 1
        
        return quadruplets

参考

Krahets - 力扣(LeetCode) (leetcode-cn.com)

以上是关于18. 四数之和的主要内容,如果未能解决你的问题,请参考以下文章

LeetCode 18. 四数之和

leetcode-----18. 四数之和

LeetCode 18. 四数之和

leetcode 每日一题 18. 四数之和

18. 四数之和

Java算法 每日一题 编号18:四数之和