---
# System prepended metadata

title: 0018. 4Sums
tags: [Leetcode, 双指针]

---

#    0018. 4Sums
###### tags: `Leetcode` `双指针`
##    思路
依然是双指针的一道题目，暴力解法的复杂度是$O(N^4)$，因此我们要想一个$O（N^3）$的方法。这道题我的思路是先固定前面两个（用回圈实现），再用双指针找sum和target一样的另外两个数（用l和r实现）。

这道题最大的问题在于答案中不能有重复的，因此就需要满足如下条件：
1.这次选到的i位置的值不能和上一次的重复
2.当固定一个i的时候，这次选到的j位置对应的值不能和上次的重复
3.l和r同理

用code表示如下
```if(i>0 && nums[i]==nums[i-1]){continue;}```
```if(j>i+1 && nums[j]==nums[j-1]){continue;}```
```while(r>0 && nums[r]==nums[r+1]){r--;}```
```while(l<nums.size()-1 && nums[l-1]==nums[l]){l++;}```

还有一点需要注意的是当确定i和j的时候，所能找到的l和r不止有一对，因此当进入到最后的else回圈的时候（代表这四个值可以被写入结果），不能用break跳出回圈，需要让l或r继续移动，(同时也要注意这一次的l对应的值不能和上一次的重复），因此就有了
```
l++;
while(l<nums.size()-1 && nums[l-1]==nums[l]){l++;}
```
个人认为这个题目的一个难点是要去限制l和r的范围，防止他找到超出size的地方而产生error，在while回圈那里我原本写的是```while（l！=r）```，结果后来出现问题才发现原来在l和r运算的过程中，l有可能直接跑到r的右边去，所以后来改成```while(l<r)```才正确。由此可以直到双指针的问题，要死死地给他限制住，不然就有可能出问题。

最后，有一个要注意的小点是要看清input的size的限制，这道题有可能给size = 0，1，2，3，所以如果没有在开头加限制，也会出现index超出范围的问题。
##    Code
```c
class Solution {
public:
    vector<vector<int>> fourSum(vector<int>& nums, int target) {
        vector<vector<int>> result;
        sort(nums.begin(),nums.end());
        if(nums.size()<4||nums[0]*4>target||nums[nums.size()-1]*4<target){
            return result;
        }
        for(int i = 0; i < nums.size()-3; i++){
            if(i>0 && nums[i]==nums[i-1]){
                continue;
            }
            for(int j = i+1; j < nums.size()-2; j++){
                if(j>i+1 && nums[j]==nums[j-1]){
                  continue;  
                }
                int l = j+1;
                int r = nums.size()-1;
                while(l<r){
                    int sum = nums[i]+nums[j]+nums[l]+nums[r];
                    if(sum > target){
                        r--;
                        while(r>0 && nums[r]==nums[r+1]){
                            r--;
                        }
                    }
                    else if(sum < target){
                        l++;
                        while(l<nums.size()-1 && nums[l-1]==nums[l]){
                            l++;
                        }
                    }
                    else{
                        vector<int> temp = {nums[i],nums[j],nums[l],nums[r]};
                        l++;
                        while(l<nums.size()-1 && nums[l-1]==nums[l]){
                            l++;
                        }
                        result.push_back(temp);
                    }
                }
            }
        }
        return result;
    }
};
```
## Improvements
经历了无数次失败（Wrong answer和runtime error（index超出范围））之后，第一次的成功在时间上只beat掉了35%的人（哭泣）。直到后来在最开始的if里面加了```nums[0]*4>target||nums[nums.size()-1]*4<target```,才变成beat了86%的人，可见开头就排除的重要性。

## Code in Java
```java
class Solution {
    public static List<List<Integer>> fourSum(int[] nums, int target) {
        Arrays.sort(nums);
        List<List<Integer>> ans = new ArrayList<>();
        int len = nums.length;
        if (len < 4 || nums[0]*4 > target || nums[len-1]*4 < target) return ans;
        for(int i = 0; i < len - 3; i++){
           if (i > 0 && nums[i] == nums[i-1]) continue;
            for(int j = i + 1; j < len - 2; j++){
                if (j > i+1 && nums[j] == nums[j-1]) continue;
                int l = j + 1;
                int r = len -1;
                while (l < r){
                    int sum = nums[i] + nums[j] + nums[l] + nums[r];
                    if(sum == target){
                        ans.add(Arrays.asList(nums[i], nums[j], nums[l], nums[r]));
                        while (l < r && nums[l] == nums[l+1]) l++;
                        while (l < r && nums[r] == nums[r-1]) r--;
                        l++;
                        r--;
                    }else if (sum > target){
                        r--;
                    }else {
                        l++;
                    }
                }
            }
        }
    return ans;
    }
}
```
## 再优化
```java
// 最开始的时候，如果最小的四个加起来已经比target大了，或者最大的四个之和比target小，那就没必要后边了
if (len < 4 || nums[0]+nums[1]+nums[2]+nums[3] > target || nums[len-1]+nums[len-2]+nums[len-3]+nums[len-4] < target) return ans;


for(int i = 0; i < len - 3; i++){
   if (i > 0 && nums[i] == nums[i-1]) continue;
   \\ 如果最小的四个加起来已经比target大了，那就没必要再loop循环了
   if (nums[i] + nums[i+1] + nums[i+2] + nums[i+3] > target) break;
    for(int j = i + 1; j < len - 2; j++){
        if (j > i+1 && nums[j] == nums[j-1]) continue;
        \\ 同理
        if (nums[i] + nums[j] + nums[j+1] + nums[j+2] > target) break;
```
