大神的总结:http://blog.csdn.net/linhuanmars/article/details/38555885
划重点:
1. 如果求出所有的结果,这个时候会涉及到重复 pair 的处理。
2. 如果重复的元素太多,用哈希表就不是很方便。所以后面的 3Sum 和4Sum 都没有选择用哈希表。
基本来说,主要是用双指针法。
- 首先判断边界条件。注意nums == null
一定要在nums.length < 2
的前面。
- 接着对原数组排序。
- 然后在循环体中,使用夹逼的方法找到合适的元素,如果题目要求有且只有一个解,那么直接输出;如果有多个解,那么要去重。去重的主要思想是:如果遇到了相邻元素相等的情况,就让指针的移动大于 1。
注意:下面的笔记中第一个 big O 是时间复杂度,第二个是空间复杂度。
https://leetcode.com/problems/two-sum/
解题思路:
solution 1 : O(n) + O(n)
- 在循环体中,查找是否有满足要求的数,如果有直接输出(因为题目说了有且只有一个答案)。solution 2 : O(nlogn) + 取决于排序算法
- 需要注意的是:这里先对数组排了序,这将打乱原来的 index 顺序。因此要做一下处理。
- 这里处理的方式为:首先将未排序的数组保存起来,接着在循环中如果发现了符合条件的数,将它们保存在变量中,然后退出循环。最后通过两次正序和逆序的遍历,找到数值原来的 index。
solution 1 : HashMap
public class Solution {
public int[] twoSum(int[] nums, int target) {
Map map = new HashMap<>();
for(int i=nums.length-1; i>=0; i--) {
if(map.containsKey(target - nums[i]))
return new int[] {i, map.get(target - nums[i])};
map.put(nums[i], i);
}
return null;
}
}
solution 2 : two pointers
public class Solution {
public int[] twoSum(int[] nums, int target) {
int[] numsCopy = nums.clone();
if(nums == null || nums.length < 2)
return null;
Arrays.sort(nums);
int tmp1 = 0, tmp2 = 0;
int low = 0, high = nums.length - 1;
while(low < high) {
if(nums[low] + nums[high] == target) {
tmp1 = nums[low];
tmp2 = nums[high];
break;
} else if(nums[low] + nums[high] < target) {
low++;
} else {
high--;
}
}
int res1 = 0, res2 = 0;
for(int i=0; iif(numsCopy[i] == tmp1 ) {
res1 = i;
break;
}
}
for(int j=numsCopy.length-1; j>=0; j--) {
if(numsCopy[j] == tmp2) {
res2 = j;
break;
}
}
return new int[] {res1, res2};
}
}
https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/
解题思路:
solution 1 : O(nlogn)
- 上面那道题的简单版,不用考虑 index 乱序的问题。
solution 1 : two pointers
public class Solution {
public int[] twoSum(int[] nums, int target) {
if(nums == null || nums.length < 2)
return null;
int low = 0, high = nums.length - 1;
while(low < high) {
if(nums[low] + nums[high] == target) {
return new int[] {low+1, high+1};
} else if(nums[low] + nums[high] < target) {
low++;
} else {
high--;
}
}
return null;
}
}
https://leetcode.com/problems/3sum/
解题思路:
solution 1 : O(n^2) + O(n)
- 使用 Two Sum 的双指针思想,只是要多一个元素的 for 循环。并且,要滤过重复元素,避免重复操作。
- 去重的具体的做法是:在 for 循环下的if(i == 0 || nums[i] > nums[i-1])
,以及两个 while:while(j < k && nums[j] == nums[j-1])
。如果两个 i( j / k ) 所指的元素是相同的,那么后者所要遍历的结果在前者已经经历过了,因此可以去掉这部分操作。
public class Solution {
public List> threeSum(int[] nums) {
List> result = new ArrayList<>();
if(nums == null || nums.length < 3)
return result;
Arrays.sort(nums);
for(int i=0; i2; i++) {
if(i == 0 || nums[i] > nums[i-1]) {
int j = i + 1;
int k = nums.length - 1;
while(j < k) {
if(nums[i] + nums[j] + nums[k] == 0) {
List list = new ArrayList<>();
list.add(nums[i]); list.add(nums[j]); list.add(nums[k]);
result.add(list);
j++; k--;
while(j < k && nums[j] == nums[j-1])
j++;
while(j < k && nums[k] == nums[k+1])
k--;
} else if(nums[i] + nums[j] + nums[k] > 0) {
k--;
} else {
j++;
}
}
}
}
return result;
}
}
https://leetcode.com/problems/3sum-closest/
解题思路:
solution 1 : O(n^2) + O(n)
- 和 3Sum 以及 Two Sum 都很类似。不过这里需要维护一个变量 diff,根据 diff 来找到与 target 最相近的 sum。
- 但是,为什么这里不用去重?
public class Solution {
public int threeSumClosest(int[] nums, int target) {
int min = Integer.MAX_VALUE, result = 0;
if(nums == null || nums.length < 3)
return 0;
Arrays.sort(nums);
for(int i=0; i2; i++) {
int j = i + 1;
int k = nums.length - 1;
while(j < k) {
int sum = nums[i] + nums[j] + nums[k];
int diff = Math.abs(target - sum);
if(diff == 0) {
return sum;
} else if(diff < min){
min = diff;
result = sum;
}
if(sum < target) {
j++;
} else {
k--;
}
}
}
return result;
}
}
https://leetcode.com/problems/4sum/
解题思路:
solution 1 :
- 和之前的思路一样,只是多了一层循环。
public class Solution {
public List> fourSum(int[] nums, int target) {
List> result = new ArrayList<>();
if(nums == null || nums.length < 4)
return result;
Arrays.sort(nums);
for(int i=0; i3; i++) {
if(i == 0 || nums[i] > nums[i-1]) {
for(int j=i+1; j2; j++) {
if(j == i+1 || nums[j] > nums[j-1]) {
int k = j+1;
int l = nums.length - 1;
while(k < l) {
if(nums[i] + nums[j] + nums[k] + nums[l] == target) {
List list = new ArrayList<>();
list.add(nums[i]); list.add(nums[j]);
list.add(nums[k]); list.add(nums[l]);
result.add(list);
k++; l--;
while(k < l && nums[k] == nums[k-1])
k++;
while(k < l && nums[l] == nums[l+1])
l--;
} else if(nums[i] + nums[j] + nums[k] + nums[l] < target) {
k++;
} else {
l--;
}
}
}
}
}
}
return result;
}
}