"The road to success and the road to failure are almost exactly the same." - Colin R. Davis
1. 题目描述
2. 题目分析与解析
2.1 思路一——暴力方法
因为三个数相加为0,那么说明其中两个加数的和与另一个加数为相反数则满足题意。所以可以得到暴力方法:两层循环相加两个数,第三层循环判断是否和与当前数是否为相反数。
但是这种算法不用想也会超时,因为复杂度已经到达了O(n^3),所以我们再来想想怎么优化。
2.2 思路二——双指针
对于这种数组的题目,因为它内容是杂乱无序的,我们应该想到将它先进行排序。因为排序后的数组这个有序的信息很可能帮助我们更快地解决题目,所以对于数组问题求解找不到思路的先想想如果它是个有序数组你会怎么做。
对于题目中给出的示例:
如果我们先将它排序,会得到:nums = [-4, -1, -1, 0, 1, 2]:
看到这里有没有一点感觉?如果我们将一个指针指向第一个数,我们只需要考虑它后面的数字中任意两个数字之和为这个指针的相反数就行。而对于有序数组而言,任意两个数字之和是某一个target的值我们之前讲过可以使用双指针分别指向头尾,向目标缩进,而且由于它只需要遍历整个数组一次,因此其时间复杂度为O(N)。再加上一个循环来遍历需要的target,那么就可以得到时间复杂度为O(N^2)的算法。
基本思路为:
-
使用指针
i
表示需要的target,从头到尾遍历数组 -
使用head与end指针,分别指向
i+1
和nums.length - 1
的位置 -
判断head与end的值的和与target的相反数(也就是nums[i]的相反数)的大小
-
如果大于
- target
,则end-- -
如果小于
- target
,则head++ -
如果相等说明head+end+target刚好为0,满足题意加入结果集,head++,end--
-
直到 end >= head 退出
-
但是我们还需要注意题目提到了:
因此我们还需要考虑重复的情况,也就是
-
对于 i 指向的target与 i + 1指向的target如果相同,我们需要排除掉,因为这回得到相同的三元组结果,因为相同的target的所有可能结果在第一次已经全部获得了。
-
同时在我们进行判断head与end求和的过程中,如果head++后的值等于head的值就需要跳过,end--后的值等于end的值也需要跳过,因为这相当于同样的加数。
因此根据上述思路就可以写处我们的代码了。
3. 代码实现
3.1 暴力解法
3.2 双指针
4. 运行结果
第一种方法会超时,第二种结果如下:
5. 相关复杂度分析
5.1 暴力解法
时间复杂度:O(N^3)
空间复杂度:O(1)
5.2 双指针
-
时间复杂度:
O(n^2)
,数组排序O(N log N)
,遍历数组O(n)
,双指针O(n)
,总体复杂度O(N log N)
+O(n)
*O(n)
=O(n^2)
-
空间复杂度:O(1)