三数之和的详细分析(java)
著作权声明
本文优化去重和剪枝条件思路参考LeetCode-Solution
题目描述
15. 三数之和:给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
示例一
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例二
输入:nums = []
输出:[]
示例三
输入:nums = [0]
输出:[]
问题分析
本题的核心关键字为和为0的三元组和不重复。
暴力解法
如果考虑暴力解法,直接用三重循环进行扫描那么该方法的最少时间复杂度为O(N3),而且还要用例如哈希表等方法进行重复过滤,也提高了空间复杂度。
优化去重
不重复的含义是什么?关键在哪里?
重复的情况包含两种:一、相同取值的不同顺序组合;二、不同位置的相同数字。
相同取值的不同顺序组合
保证不重复即保证每次遍历的三元组顺序的唯一性,在什么样的情况下可以保证这个唯一性呢?核心就在顺序上,我们可以提前设定好数组的排序顺序,在这种情况下我们在其上按照预定的遍历规则进行扫描就可以保证其唯一性。
继续沿用三重循环,按照如下规则取值遍历:
- 第二重循环枚举到的元素不小于当前第一重循环枚举到的元素;
- 第三重循环枚举到的元素不小于当前第二重循环枚举到的元素。
也就是说,我们取的三元组 (a, b, c)满足a≤b≤c,保证了只有 (a, b, c)这个顺序会被枚举到,而 (b, a, c)、(c, b, a)等等将不会被扫描到,这样就减少了重复。
不同位置的相同数字
例如对于这个数组来说:[0, 1, 2, 2, 2, 3]
,我们枚举到的第一个三元组为(0,1,2),如果第三重循环继续遍历下个元素的话,那么取到三元组仍然为(0, 1, 2)。而我们需要跳过重复的数字,直接枚举(0,1,3)。
对于每一重循环而言,由于已经进行了排序,相同的数字的位置也是相邻的,而对于这些相同的数字,我们只需枚举一次即可,所以如果需要的相邻的数字是相同的,后面的相同的数字会被跳过,这样就进一步减少了重复。
减少循环
如何减少循环以达到优化时间复杂度的效果,常用的有以空间换时间还有使用剪枝条件,现在让我们来思考这两种方式。
以空间换时间
考虑另外一个相似的题目两数之和1.两数之和,在该题目中用到了使用哈希表来存储遍历过的值,直接通过哈希表来寻找目标数的方法,这样使得时间复杂度达到了O(n)。那么这个题目能不能用这种方法呢。
在前面的优化去重不变的情况下,使用两重循环,在第二重循环中引进哈希表储值的方法。代码如下:
List<List<Integer>> res = new ArrayList<List<Integer>>();