【leetcode】15 3sum
前言
最近在刷leetcode,刷题的过程不免遇到一些感觉很困难的题目,在此记录一下解法与思路。
题目描述
https://leetcode-cn.com/problems/3sum/description/
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
解法1
最容易想到的解法就是暴力解法。
首先将数组排序,然后三重循环遍历,找到满足条件的元素。
最后还需要对结果再次排序并去重。
由于三个元素,时间复杂度为O(n^3),但如果数组数据量过大,提交会超时。
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
sort(nums.begin(),nums.end());
for (int i = 0; i < nums.size(); i ++)
{
for (int j = i + 1; j < nums.size(); j ++)
{
for (int k = j + 1; k < nums.size(); k ++)
{
if (nums[i] + nums[j] + nums[k] == 0){
res.push_back({nums[i],nums[j],nums[k]});
}
}
}
}
//先排序再去重
sort(res.begin(),res.end());
res.erase(unique(res.begin(),res.end()),res.end());
return res;
}
解法2
使用map来保存数组元素及每个元素的个数。
然后对数组进行排序与去重。
最后进行双重循环,再在map中查找第三个值。
时间复杂度:O(n^2)。
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
unordered_map<int,int> map1;
for(int e:nums)
map1[e] ++;
//特殊情况单独处理
if (map1[0] >= 3)
res.push_back({0,0,0});
sort(nums.begin(),nums.end());
nums.erase(unique(nums.begin(),nums.end()),nums.end());
for (int i = 0; i < nums.size(); i ++)
{
for (int j = i + 1; j < nums.size(); j ++)
{
//由于不存在三个同样的非零值和为0
//但存在两个同样的非零值与其他值的和为0
//需要对这种特殊情况单独作判断
if (map1[nums[i]] >= 2 && nums[i] * 2 + nums[j] == 0)
res.push_back({nums[i],nums[i],nums[j]});
if (map1[nums[j]] >= 2 && nums[j] * 2 + nums[i] == 0)
res.push_back({nums[i],nums[j],nums[j]});
//如果存在c值,且需要保证c值位置在j之后,不能使用已经遍历过的元素
int c = 0 - nums[i] - nums[j];
if (map1[c] > 0 && c > nums[j]){
res.push_back({nums[i],nums[j],c});
}
}
}
return res;
}
解法3
这是推荐的解法,首先进行最外层遍历。
内层采用对撞指针的方法,进行遍历。
时间复杂度:O(n^2)。
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if(nums.empty() || nums.size() < 3)
return res;
sort(nums.begin(),nums.end());
for (int i = 0; i < nums.size() - 2; i ++)
{
int tar = 0 - nums[i];
/*
* 以下为获取两个数和为tar
* 对撞指针,向中间移动
*/
int j = i + 1,k = nums.size() - 1;
while (j < k)
{
if (nums[j] + nums[k] == tar){//若和等于tar时,则可保存数据
res.push_back({nums[i],nums[j ++],nums[k --]});
while(j < k && nums[j] == nums[j - 1])// 若有相等的,则向右移动
j ++;
while(j < k && nums[k] == nums[k + 1])// 若有相等的,则向左移动
k --;
}else if (nums[j] + nums[k] > tar){//若和大于tar,则需要较大值减小,右边界向左移动
k --;
}else{//若和小于tar,则需要较小值增大,左边界向右移动
j ++;
}
}
//保证下一个为不重复的数据
while (i < nums.size() - 2 && nums[i] == nums[i + 1])
i ++;
}
return res;
}