例题1. 1 两数之和 Easy
例题2. 167 两数之和,输入的是有序数组 Easy
例题3. 15 三数之和 Medium
例题4. 16 最接近的三数之和 Medium
例题5. 18 四数之和 Medium
例题1. 1 两数之和 Easy
例题2. 167 两数之和,输入的是有序数组 Easy
例题3. 15 三数之和 Medium
【题目】
【方法1:三重循环】
时间复杂度o(n^3)。略。
【方法2:排序+双指针】
首先对数组进行排序。对于每一个nums[i],使用双指针找到两个数,和为-nums[i]。
时间复杂度:排序为o(nlogn);一共n个数,对每个数查找为o(n),n个数查找时间为o(n2)。总时间复杂度为o(n2)。
代码如下:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int len = nums.length;
List<List<Integer>> res = new LinkedList<List<Integer>>();
if(len <=2) return res; //返回空列表,不要返回null.
Arrays.sort(nums);
int i,j;
for(int cur=0;cur<len;cur++){
if(nums[cur]>0) break; //因为已经排好序,当nums[cur]>0时,后面的肯定都>0,肯定没有解了。
if(cur>0 && nums[cur] == nums[cur-1]) continue; //为了防止重复。详见解释1。
i=cur+1;j=len-1; //i要从cur+1开始。如果i从cur的前面开始的话(比如位置a),那就会与cur=a时一个结果重复。详见解释2。
while(i<j){
if(nums[i]+nums[j] == -nums[cur]){
//得到了一个结果。
List li = new LinkedList<>();
li.add(nums[cur]); li.add(nums[i]); li.add(nums[j]); res.add(li);
//跳过重复的nums[i]或nums[j],防止重复。详见解释3。
while(i<j && nums[i+1] == nums[i]){
i++;
}
i++;
/*这段代码有没有都行。因为光走i就可以打破平衡了。
while(i<j && nums[j-1]== nums[j]){
j--;
}
j--;
*/
}else if(nums[i]+nums[j]<-nums[cur]){
i++;
}else{
j--;
}
}
}
return res;
}
}
解释1:
比如:
当cur为1时,nums[cur]=-1,找到了一组答案为[-1, 0, 1]。
此时cur++,变为2,nums[cur]还是-1。这样就会得到重复答案[-1, 0, 1]
解释2:
比如
假设a+b+c = 0。当前的cur指向b。如果i是从头开始找的话,会得到结果[a, b, c]。
因为cur是从0遍历过来的,所以在cur指向b之前,已经指向过a:
当时呢,肯定就会有答案[a, b ,c]。
这样就导致重复。
解释3:
比如
此时,nums[cur]为-2。nums[i]=0,nums[j]=2。得到一个结果[-2, 0 , 2]
当i++的时候,nums[i]还是0,还是会得到重复结果[-2, 0, 2]。
所以做法就是让i把后面的0跳过去。i跳过0值,j是不是跳过2已经无所谓了。
运行结果:
例题4. 16 最接近的三数之和 Medium
【题目】
【分析】
与三数之和一样。首先排序,然后双指针。
找的过程中维护一个全局的变量:当前与target距离最近的三数和。
时间复杂度o(n2)
【代码】
class Solution {
public int threeSumClosest(int[] nums, int target) {
int len = nums.length;
Arrays.sort(nums);
int i,j;
int res=nums[0]+nums[1]+nums[2]; //保存到目前为止距离target最近的三数之和
int tmperr = Math.abs(target - res); //保存到目前为止的最小误差
for(int cur=0; cur<len; cur++){
i=cur+1;j=len-1;
while(i<j){
int sum = nums[cur]+nums[i]+nums[j];
if(sum == target){
return target;
}else if(sum < target){
i++;
}else{
j--;
}
if(Math.abs(target-sum) < tmperr){
res = sum;
tmperr = Math.abs(res - target);
}
}
}
return res;
}
}
结果:
例题5. 18 四数之和 Medium
【题目】
【分析】
转化成3 sum问题。
首先排序,然后通过一层循环固定一个数cur。然后解决 a+b+c = target-cur的3 sum问题。
解决3sum问题,就是一层循环固定一个数,然后通过双指针找。
【代码】
class Solution {
public List<List<Integer>> fourSum(int[] nums, int target) {
int len = nums.length;
List<List<Integer>> res = new LinkedList<List<Integer>>();
if(len <=3) return res;
Arrays.sort(nums);
if(nums[len-1]+nums[len-2]+nums[len-3]+nums[len-4]<target) return res; //最大的四个数都不够大,肯定就没有了。
int i,j,k;
for(int cur = 0; cur<len-3 ;cur++){
if(cur>0 && nums[cur]==nums[cur-1]) continue;
if(nums[cur]+nums[cur+1]+nums[cur+2]+nums[cur+3] > target) break; //最小的四个数都不够小,肯定没有了。
for(i=cur+1;i<len-2;i++){
if(i>cur+1 && nums[i] == nums[i-1]) continue;
if(nums[i]+nums[i+1]+nums[i+2]> target-nums[cur]) break; //最大的三个数不够大,就没有了。
if(nums[len-1]+nums[len-2]+nums[len-3]< target-nums[cur]) break; //最小的三个数不够小,就没有了。
j = i+1;
k = len-1;
while(j<k){
if(nums[j]+nums[k] == target-nums[cur]-nums[i]){
List<Integer> li = new LinkedList<Integer>();
li.add(nums[cur]);li.add(nums[i]);li.add(nums[j]);li.add(nums[k]);res.add(li);
while(j<k && nums[j+1]==nums[j]){
j++;
}
j++;
}else if(nums[j]+nums[k]< target -nums[cur] - nums[i]){
j++;
}else{
k--;
}
}
}
}
return res;
}
}
代码跟3sum问题差不多,细节可以参考哪里的解释。
结果: