求陣列中多個數相加等於某一值
主要思想:排序+兩端逼近
內容:排序使得整個陣列有序從而可以使用雙指標從陣列的兩端向中間逼近所需要的值
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;i<n;i++){
map [nums[i]]=i;
}
for(int i=0;i<n;i++){
//去重
if(i>0&&nums[i]==nums[i-1]) continue;
//第二個數
for(int j=i+1;j<n;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();a<last-2;a++){
if(*a==*(a-1) && a>nums.begin()) continue;
auto b=a+1;
auto c=last-1;
while(b<c){
if((*a+*b+*c)<target){
b++;
//注意這裡去重的方式
while(*b==*(b-1) && b<c) b++;
}
else if((*a+*b+*c)>target){
c--;
while(*c==*(c+1) && c>b) c--;
}
else{
// vector<int> 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) && b<c) b++;
while(*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();a<last-2;a++){
auto b=a+1;
auto c=last-1;
while(b<c){
if(abs(target-(*a+*b+*c))<max){
offset=target-(*a+*b+*c);
max=abs(offset);
}
if((*a+*b+*c)<target){
b++;
}
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();a<last-3;a++){
if(*a==*(a-1) && a>nums.begin()) continue;
for(auto b=a+1;b<last-2;b++){
if(*b==*(b-1) && b>a+1) continue;
auto c=b+1,d=last-1;
while(c<d){
int val=*a+*b+*c+*d;
if(val<target){
c++;
while(*c==*(c-1) && c<d) c++;
}
else 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) && c<d) c++;
while(*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)<target) 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)<target) 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<n;){
//找到需要的值
if(nums[i]==val){
offset++;
for(int j=i+1;j<n;j++){
nums[j-1]=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<n;i++){
//找到需要的值
if(nums[i]!=val){
nums[index++]=nums[i];
}
}
return index;
}
};