文章目录
15. 3Sum 三数之和
15. 3Sum
Given an array nums
of n integers, are there elements a, b, c in nums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
双指针
本题的难点有两点
- 如何避免暴力破解
- 如何保证最后得出的三个数的值不重复
这里使用了双指针的方式。首先先将所给的数组进行排列,去最左边的值作为参考值,然后除去参考值后设置一左一右两个指针,排列后容易就知道左指针所指的数字大于右指针所指的数值。比较指针所指的两值之和与参考值的大小,来调整两个指针所指的位置,最后面得出结果。在处理的时候还有一点需要注意的就是重复的数值。排序过后,相等的数值必然相邻。我们要保证左指针指向重复数字的最右边的数字,右指针指向重复数字最左边的数字。
回到上面所说的两个难点。首先暴力破解时间复杂度必然是O(n^ 3)。设置双指针,省略了一重循环,可以降到O(n^2)。对于数值不重复,首先选取最左的数字为参考值,因此第一个数字可以保证不重复(相同数字就去最右的)。其次使用左右指针相互逼近的方式求值,因此左指针的值必然小于等于右指针的值,且对于某一参考值,左指针的值是从小到大增长的,因此第二个值也不可能重复。第三个值同理。
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
int numsLen = nums.length;
List<List<Integer>> result = new ArrayList<>();
for(int i = 0 ; i < numsLen - 2; i++){
if(i > 0 && nums[i] == nums[i-1]){
continue;
}
int target = -nums[i];
int left = i + 1;
int right = nums.length -1;
while(left < right){
if(target < nums[left] + nums[right]){
right --;
} else if(target > nums[left] + nums[right]){
left ++;
} else {
result.add(Arrays.asList(nums[i],nums[left],nums[right]));
left++;
right--;
while(left < right && nums[left] == nums[left - 1]){
left++;
}
while(left < right && nums[right] == nums[right + 1]){
right--;
}
}
}
}
return result;
}
}
使用Map
和上面使用双指针的方法一样。唯一不同的地方在于使用Map去掉了重复的值,这样子就可以省略当遇到相同数字的时候之后指针需要进行位移的操作。因为使用了Map,效率会低很多,而且本质上还是使用了双指针的方式。
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
Arrays.sort(nums);
List<List<Integer>> r=new ArrayList<>();
HashMap<Integer,Integer> index=new HashMap<>();
for(int i=0;i<nums.length;i++)
index.put(nums[i], i);
for(int i=0;i<nums.length-2;i++){
for(int j=i+1;j<nums.length-1;j++){
int target=0-nums[i]-nums[j];
if(index.containsKey(target) && index.get(target)>j){
r.add(Arrays.asList(nums[i], nums[j], target));
j=index.get(nums[j]);
}
i=index.get(nums[i]);
}
}
return r;
}
}