15. 三数之和
一开始看到这个题当然会想到经典的两数之和,那道题用的是哈希表,不过这道题和那道题有很大的不同,两数之和要求的是索引,且不存在重复的答案,而三数之和中非常重要的就是去重。
首先在不考率去重的情况下怎么做呢,当然可以直接暴力求解,时间复杂度为O(N ^ 3),如果有三指针的话时间复杂度会降低到O(N^2),如何用三指针呢,其实就是先排序,然后第一个指针从左往右遍历,每次遍历先固定好第一个指针,然后另设两个指针,一个指向末尾,一个指向第一个指针的下一个元素,然后这两个指针依次往中间逼近看能否得到满足要求的三个数,逼近的方法是当前三个指针指向元素的和若大于0则末尾指针左移,若小于0则左边的那个指针右移(因为排了序的所以这样移动可以往0逼近)。
那么如何去重呢,在上面我们已经排序了,那么相同的元素一定会紧挨在一起,我们要做的就是对每个指针跳过相同的元素,因为相同的元素只需要判断一次即可。
代码如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
sort(nums.begin(), nums.end());
int n = nums.size();
vector<vector<int>> result;
for (int i = 0; i < n - 2; i++) {
if (i > 0 && nums[i] == nums[i - 1])
continue;
int left = i + 1, right = n - 1;
while (left < right) {
if (nums[i] + nums[left] + nums[right] > 0) right--;
else if (nums[i] + nums[left] + nums[right] < 0) left++;
else {
result.push_back({nums[i], nums[left], nums[right]});
while (left < right && nums[right] == nums[right - 1]) right--;
while (left < right && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
return result;
}
};
我们这里只在三数之和等于0的情况下去重,去重的步骤就是一直判断两边的指针是否与下一个相同,如果相同则跳过。其实还可以再加一个剪枝优化性能,即如果当前的nums[i]已经大于0,那么就可以直接break了,因为是排序后的数组,之后的数字以及和肯定都大于0了。
18. 四数之和
其实四数之和和三数之和的解法是差不多的,只不过多了一个循环,也可以说用的是四指针,在遍历的过程中固定两个指针,然后另外两个指针的行为就和三数之和差不多了。也可以使用剪枝,即当前和以及大于target并且和也大于0了,那么就可以直接break掉了(和必须也要大于0,因为如果小于0说明和还有可能继续变小以满足要求)。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int n = nums.size();
sort(nums.begin(), nums.end());
vector<vector<int>> result;
for (int i = 0; i < n - 3; i++) {
if (i > 0 && nums[i] == nums[i - 1])
continue;
if (nums[i] > target && nums[i] > 0)
break;
for (int j = i + 1; j < n - 2; j++) {
if (j > i + 1 && nums[j] == nums[j - 1])
continue;
if (nums[i] + nums[j] > target && nums[i] + nums[j] > 0)
break;
int left = j + 1, right = n - 1;
while (left < right) {
if ((long)nums[i] + nums[j] + nums[left] + nums[right] > target) right--;
else if ((long)nums[i] + nums[j] + nums[left] + nums[right] < target) left++;
else {
result.push_back({nums[i], nums[j], nums[left], nums[right]});
while (left < right && nums[right] == nums[right - 1]) right--;
while (left < right && nums[left] == nums[left + 1]) left++;
right--;
left++;
}
}
}
}
return result;
}
};