算法思想:
1.a+b+c=0,自由度为2,所以可以不用三循环,而使用双循环;
2.先给数组从小到大排序,即保证a≤b≤c,从而避免答案出现[a,b,c],[b,c,a]…等重复数组;
3.固定a后,寻找b,c的过程可以利用双指针,b和c分别从前往后和从后往前枚举,从而避免重复查找。
灵感:当出现互为限制的两个变量(即自由度为1)的查找问题时,可以考虑利用双指针。
python
class Solution:
def threeSum(self, nums):
n = len(nums)
nums.sort() # 为避免重复输出,从小到大对输入数组排序
ans = list()
for first in range(n): # a指针,从前往后
if first > 0 and nums[first] == nums[first-1]:
# 避免本次枚举数字的上次一样,从而避免重复
continue
third = n-1 # c指针从后往前,从而将三个循环减少为两个
target = 0 - nums[first]
for second in range(first+1,n): # b指针,从前往后
if second > first+1 and nums[second] == nums[second-1]:
# 避免本次枚举数字的上次一样,从而避免重复
continue
while second < third and nums[second] + nums[third] > target:
# b指针要在c指针左侧,避免重复查找
''' 由于nums排序为从小到大,故 nums[second] + nums[third] > target
可以判断b,c是否符合条件 '''
third -= 1
if second == third:
break # 第二个循环停止
if nums[second] + nums[third] == target:
ans.append([nums[first], nums[second], nums[third]])
return ans
- 用二数之和做递归可以解决所有Nsum问题!
class Solution:
def threeSum(self, nums: List[int]):
nums = sorted(nums)
target = 0
def N_Sum(nums, n, start, target):
length = len(nums)
res = []
if n < 2 or length < n:
return res
if n == 2:
l, r = start, length - 1
while l < r:
left, right = nums[l], nums[r]
sum = nums[l] + nums[r]
if sum == target:
res.append([left, right])
while l < r and nums[l] == left:
l += 1
while l < r and nums[r] == right:
r -= 1
elif sum < target:
while l < r and nums[l] == left:
l += 1
else:
while l < r and nums[r] == right:
r -= 1
else:
i = start
while i < length:
sub = N_Sum(nums, n-1, i+1, target-nums[i])
for arr in sub:
arr.insert(0, nums[i])
res.append(arr)
while i < length - 1 and nums[i] == nums[i+1]:
i += 1
i += 1
return res
return N_Sum(nums, 3, 0, target)
C++
class Solution {
public:
vector<vector<int> > threeSum(vector<int> &num) {
vector<vector<int>> res;
if(num.size() < 3) return res;
sort(num.begin(), num.end());
for(int i = 0; i < num.size() - 2; i++){
int j = i + 1;
int k = num.size() - 1;
int target = -num[i];
while(j < k){
if(num[j] + num[k] > target) k--;
else if(num[j] + num[k] < target) j++;
else{
vector<int> curr = {num[i], num[j], num[k]};
res.push_back(curr);
while(j + 1 < k && num[j] == num[j + 1]) j++;
while(k - 1 > j && num[k] == num[k - 1]) k--;
j++, k--;
}
}
while(i + 1 < num.size() - 2 && num[i] == num[i + 1]) i++;
}
return res;
}
};
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
// 先排序,以防止重复结果
sort(nums.begin(), nums.end());
return nSum(nums, 3, 0, 0);
}
vector<vector<int>> nSum(vector<int>& nums, int n, int start, int target) {
// 实现了一个通用递归函数求nSum问题
int length = nums.size();
vector<vector<int>> res;
// 特殊情况跳出
if (n < 2 || length < n) return res;
//n==2是basecase
if (n == 2) {
int left = start, right = length - 1;
while (left < right) {
int lo = nums[left], hi = nums[right];
int tmp = lo + hi;
// 以下while循环都是在跳过重复元素
if (tmp == target) {
res.emplace_back(vector<int>{nums[left], nums[right]});
while (left < right && nums[left] == lo) left++;
while (left < right && nums[right] == hi) right--;
} else if (tmp < target) {
while (left < right && nums[left] == lo) left++;
} else {
while (left < right && nums[right] == hi) right--;
}
}
} else {
// n>2 需要递归到n-1数之和问题
for (int i = start; i < length; i++) {
vector<vector<int>> arrs = nSum(nums, n-1, i+1, target-nums[i]);
for (auto& arr : arrs) {
arr.emplace_back(nums[i]);
res.emplace_back(arr);
}
// 跳过重复元素
while (i < length - 1 && nums[i] == nums[i+1]) i++;
}
}
return res;
}
};
复杂度分析:
- 时间复杂度:O(N2),其中 N是数组nums 的长度。
- 空间复杂度:O(logN),我们忽略存储答案的空间,额外的排序的空间复杂度为O(logN)。然而我们修改了输入的数组nums,在实际情况下不一定允许,因此也可以看成使用了一个额外的数组存储了nums 的副本并进行排序,空间复杂度为 O(N)。