15. 3Sum Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != ji != k, and j != k, and  ums[i] + nums[j] + nums[k] == 0`.

Notice that the solution set must not contain duplicate triplets.

题目目测可以找到 复杂度的解法。 重点在于如何去除重复triplets:

  • 遍历前两个元素并利用set可以达到 的复杂度。
  • 无论如何可以先排序,的复杂度完全可以忽略
  • 容易想到遍历前两个元素,并用hashmap寻找第三个元素
  • 使用hashmap需要着重考虑怎么排除重复triplets

方法一 Hashmap

利用hashmap, 本身找第三个元素并不需要hashmap的value部分,但用hashmap去重是个麻烦的问题,我们可以用hashmap存储元素最后出现的index 。这样只需要判断第三个元素的index是否大于第二个index即可保证无重复

vector<vector<int>> answer;
for(int i = 0 ; i < nums.size() - 2 ; ++i){
	if(nums[i] > 0){ //这条让排名从50%上升到80%
 break;
	}
	for(int j = i + 1 ; j < nums.size() - 1 ; ++j){
 int required = -1*(nums[i] + nums[j]);
 if(hashMap.count(required) && hashMap.find(required)->second > j){ 
 //If it exists in hashmap and its last occurrence index > 2nd fixed index, we found our triplet.
 answer.push_back({nums[i] , nums[j] , required});
 }
 j = hashMap.find(nums[j])->second; //Update j to last occurence of 2nd fixed number to avoid duplicate triplets.
	}
	i = hashMap.find(nums[i])->second; //Update i to last occurence of 1st fixed number to avoid duplicate triplets.
}

方法二 Two Pointers

看到答案才意识到双指针的用法。同样遍历第一个元素,二三元素使用双指针。由于提前排序,可以通过移动上下界使得和逐渐逼近0。同样由于排序,一样的元素在一块,当发现双指针和上一个指向的位置一样的时候直接跳过就好了,对于第一个元素的遍历同理。由于元素是排好序的,且指针 ,所以只要保证没有triplet被跳过即可。 如果triplet有元素相同,有两种情况:

  • 三种情况都至少会被处理一次均不需要特殊处理
sort(nums.begin(), nums.end());
    for (int i = 0; i < n - 1; i++) {
      int l = i + 1, r = n - 1;
      while (l < r) {
        if (nums[i] + nums[l] + nums[r] > 0) {
          r--;
        } else if (nums[i] + nums[l] + nums[r] < 0) {
          l++;
        } else {
          ans.push_back({nums[i], nums[l], nums[r]});
          int tempIndex1 = l, tempIndex2 = r;
          while (l < r && nums[l] == nums[tempIndex1])
            l++;
          while (l < r && nums[r] == nums[tempIndex2])
            r--;
        }
      }
      while (i + 1 < n && nums[i] == nums[i + 1])
        i++;
    }