四个数的和
难度:中等 思路:头尾逼近法
一、问题描述
给定一个包含n
个整数的数组nums
和一个目标值target
,判断nums中是否存在四个元素a,b,c,和d,使得a+b+c+d
的值与target相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例:
给定数组nums=[1, 0, -1, 0, -2, 2], 和target=0。
满足要求的四元组集合为:
[[-1 ,0, 0, 1],
[-2, -1, 1, 2],
[-2 ,0, 0, 2]]
二、问题分析
在LeetCode的题目顺序有一个由浅入深的过渡,这题的解决方法完全可以参照三个数的求和问题。让我们来回顾一个三个数的求和问题,三个数的求和与四个数的求和表面上是一模一样的,无非是把三个数替换为四个数,所以在解决方式上内核机制也是相同的。
2.1 排序
数的求和问题,最简单粗暴的方法就是把给定的数组for循环遍历几次,找到所有符合要求的数,但是因为时间开销过大,提交的时候不被通过,所以只能找其他的渠道。第一步,将所给的数按照从小到大排序,Python有内置的排序函数,这里就直接调用了。
2.2 开始遍历
在多个数求和问题中,使用的方法为两头法,具体实现方法如下。首先固定第一个数,从数组的头部开始,求出目标数target与第一个数的差值diff;然后固定第二个数,求出之前的差值diff与当前数的差值作为新目标goal;然后使用头尾法,如果头尾的和小于goal,则头往后移,如果头尾的和大于goal,则尾往前移(经过排序后,尾部的数大头部的数小),如果头尾的和正好等于goal,那么我们找到了目标数,把四个数存到结果中,同时往中间移动头尾的坐标,头尾位置下标相等时此次遍历结束;然后依次移动前两个固定的数。
2.3 难点分析
这题确定了算法,还是有很多细节需要推敲,而这些细节有些时候比算法本身更花时间。在题目描述中,它特地提出让我们注意解决重复元素的问题,解决重复问题有两个思路:一是处理返回的数组,将其中重复的元素去掉;二是从源头上解决问题,在生成结果数组时,一旦发现重复就不再添加入返回数组。这题真正的难点就在这里了,怎么样才能最有效的去除重复元素呢?这里只提供一个巧妙的思路,重复的元素从何而来,归根结底是因为数组中本身就有重复的元素,在求和的时候,当我们移动到下一个元素的时候,检测当前元素与上一个元素是否相等,这里为什么是与前一个元素作比较而不是与后一个元素作比较,这是有原因的,如果与后一个元素作比较的话,那么就会漏掉一种情况,这两个数相等但是他们的和就是我们想找的,如果与后一个元素作比较的话这个数还没有参与计算就会被跳过。结果会错误。(注意,当我们访问一个元素的时候要确保这个元素存在否则就会报错),如果相等就跳过此次循环。(需要确保每个不重复元素都被访问到)
2.4 细节
首先要对输入的数组进行长度判断,避免输入空数组时后续进行不存在位置的的访问导致程序崩溃;其次是重复元素的问题,在元素移动过程中,为了不产生重复的四个数组合,固定的第一个数往后移动时,需要考虑当前数与上一个数是否相等,如果相等,则使用continue
语句跳过此次循环。固定的第一个数也存在一模一样的问题。其次是首尾移动时,当检测到首尾之和相等时,头部和尾部的位置都往中间移,这时头尾分别与后一个数作比较,如果相等的话,头尾坐标继续往中间靠。(这里检测到头尾之和与目标相等时,不存在一个数还没用到就被丢弃,所以可以与后一个数作比较)
三、代码分析
1 | def fourSum(self, nums, target): |
复杂度分析:
耗时还是非常多的,排序+O(N^3^)。