主要思想:排序+两端逼近
内容:排序使得整个数组有序从而可以使用双指针从数组的两端向中间逼近所需要的值
7、3Sum
顾名思义,求数组中3个数相加等于某一特定的数
自己写了一个似乎是O(n^2) 汗颜
在Two Sum的引导下,我成功使用了unordered_map
然而在run code的时候就发现了一个无法解决的问题:
Your input [-1,0,1,2,-1,-4]
Your answer [[-1,-1,2],[-1,0,1],[-1,0,1]]
Expected answer [[-1,-1,2],[-1,0,1]]
也就是这种,即使在排序过后的情况下,仍然无法解决重复问题(因为有多个-1)
考虑到nums[i]==nums[i-1]的情况,这个是不需要再判定的,似乎可以去重?
于是,我这种思路是有问题的
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int> > result;
unordered_map<int,int> map;
int n=nums.size();
sort(nums.begin(),nums.end());
for(int i=0;imap[nums[i]]=i;
}
for(int i=0;i//去重
if(i>0&&nums[i]==nums[i-1]) continue;
//第二个数
for(int j=i+1;j//第三个数
int target2=0-nums[i]-nums[j];
//第三个数字不能和第一第二个是同一个
if(map.find(target2)!=map.end() && map[target2]!=i && map[target2]>j){
//加入数据
vector<int> tre;
tre.push_back(nums[i]);
tre.push_back(nums[j]);
tre.push_back(target2);
else result.push_back(tre);
}
}
}
return result;
}
};
事实上这种不可以
Input:
[0,0,0,0]
Output:
[[0,0,0],[0,0,0]]
Expected:
[[0,0,0]]
正确的思路是在Two Sum中没有使用的左右夹逼方法,首先对数组进行一趟快排,利用*a,*b,*c ,ab在左侧,c在右侧分别向中间靠拢,如果目的比target小,说明需要b++,否则c–;
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
int n=nums.size();
if(n<3) return result;
sort(nums.begin(),nums.end());
int target=0;
auto last=nums.end();
for(auto a=nums.begin();a2;a++){
if(*a==*(a-1) && a>nums.begin()) continue;
auto b=a+1;
auto c=last-1;
while(bif((*a+*b+*c)//注意这里去重的方式
while(*b==*(b-1) && belse if((*a+*b+*c)>target){
c--;
while(*c==*(c+1) && c>b) c--;
}
else{
// vector temp;
// temp.push_back(*a);
// temp.push_back(*b);
// temp.push_back(*c);
// result.push_back(temp);
//其实可以这样向vector插入数据
result.push_back({*a,*b,*c});
b++;c--;
while(*b==*(b-1) && bwhile(*c==*(c+1) && c>b) c--;
}
}
}
return result;
}
};
8、16. 3Sum Closest
思路和上一题完全相同,使用快排+左右夹逼,复杂度O(n^2)
class Solution {
public:
int threeSumClosest(vector<int>& nums, int target) {
int n=nums.size();
sort(nums.begin(),nums.end());
auto last=nums.end();
int max=INT_MAX;
int offset=0;
for(auto a=nums.begin();a2;a++){
auto b=a+1;
auto c=last-1;
while(bif(abs(target-(*a+*b+*c))abs(offset);
}
if((*a+*b+*c)else if((*a+*b+*c)>target){
c--;
}
else{
return target;
}
}
}
return target-offset;
}
};
9、4Sum
来了…问4个数组合起来成一个数值大小的情况
于是照着前一种写,用了200多ms才跑完
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int> > result;
int n=nums.size();
if(n<4) return result;
//排序
sort(nums.begin(),nums.end());
auto last=nums.end();
for(auto a=nums.begin();a3;a++){
if(*a==*(a-1) && a>nums.begin()) continue;
for(auto b=a+1;b2;b++){
if(*b==*(b-1) && b>a+1) continue;
auto c=b+1,d=last-1;
while(cint val=*a+*b+*c+*d;
if(valwhile(*c==*(c-1) && celse if(val>target){
d--;
while(*d==*(d+1) && d>c) d--;
}
else{
result.push_back({*a,*b,*c,*d});
c++;d--;
while(*c==*(c-1) && cwhile(*d==*(d+1) && d>c) d--;
}
}
}
}
return result;
}
};
然后题解给出的是hashmap优化,感觉代码很晦涩,用了大量stl并不稀饭
于是看了discussion区,发现只要经过剪枝之后这个方法速度可以到24ms
。。。。
for(auto a=nums.begin();a<last-3;a++){
if(*a==*(a-1) && a>nums.begin()) continue;
//太大剪枝,当从当前出发的4个数据已经不可能等于target,break
if(*a+*(a+1)+*(a+2)+*(a+3) >target) break;
//太小剪枝,当前的数据+最大的三个数也不可能到达target,continue
if(*a+*(last-3)+*(last-2)+*(last-1)continue;
for(auto b=a+1;b<last-2;b++){
if(*b==*(b-1) && b>a+1) continue;
//剪枝
if(*a+*b+*(b+1)+*(b+2) >target) break;
if(*a+*b+*(last-2)+*(last-1)continue;
。。。。。
}
};
事实证明这个剪枝很有效
10、27. Remove Element
从数组中移除元素问题!在总结的第一个文章里面就是,并且将元素提至数组前
是个双指针问题!!!又踩坑了…
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n=nums.size(),temp;
// sort(nums.begin(),nums.end());
int offset=0;
for(int i=0;i//找到需要的值
if(nums[i]==val){
offset++;
for(int j=i+1;j1]=nums[j];
}
n--;
}
else i++;
}
return n;
}
};
使用index记录整个数组一共多少元素,而不需要对数组元素进行移动!!!
切记此删除数组元素的方法
class Solution {
public:
int removeElement(vector<int>& nums, int val) {
int n=nums.size(),temp;
int index=0;
for(int i=0;i//找到需要的值
if(nums[i]!=val){
nums[index++]=nums[i];
}
}
return index;
}
};