- 題目描述:四數之和
給定一個包含 n 個整數的數組 nums 和一個目標值 target,判斷 nums 中是否存在四個元素 a,b,c 和 d ,使得 a + b + c + d 的值與 target 相等?找出所有滿足條件且不重複的四元組。
注意:
答案中不可以包含重複的四元組。
示例:
給定數組 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
滿足要求的四元組集合爲:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
來源:力扣(LeetCode)
鏈接:https://leetcode-cn.com/problems/4sum
著作權歸領釦網絡所有。商業轉載請聯繫官方授權,非商業轉載請註明出處。
- C++編程實現:
排序+雙指針
和三數之和一樣,本題的難點依舊在於如何去除重複解
取兩個數組合,將問題轉化爲三數之和
算法流程:
特判,對於數組長度n,如果數組爲Null或者數組長度小於4,返回[]。
對數組進行排序。
遍歷排序後數組:
對於重複元素,跳過,條件:i>0且nums[i]==nums[i−1],避免出現重複解
二次遍歷,重複元素跳過,判斷重複元素從i後第二個元素開始,所以條件:j−i>1且nums[j]==nums[j−1]
令左指針L=j+1,右指針R=n−1,當L<R時,執行循環:
*當nums[i]+nums[j]+nums[L]+nums[R]==target時,將結果加入res並執行循環,判斷左界和右界是否和下一位置重複,以去除重複解。並同時將L,R移到下一位置,尋找新的解
*若和大於0,說明nums[R]太大,R左移
*若和小於0,說明nums[L]太小,L右移
剪枝條件:
對於本題,按照上述流程寫下來,可以通過。
我們繼續對算法進行剪枝優化
第一次遍歷
若nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,則可以退出,因爲最小四數之和大於目標,則不可能存在結果。**注意:**和三數之和的優化條件不同,三數之和中target=0,所以只要nums[i]>0,則可退出,這裏則需要更爲嚴格的條件。
若當前值和數組中最大的三個值相加依舊小於目標,nums[i]+nums[n−1]+nums[n−2]+nums[n−3]<target,則continue
第二次遍歷
同理,若nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,break
nums[i]+nums[j]+nums[n−1]+nums[n−2]<target,continue
複雜度分析
時間複雜度:O(n3),數組排序O(NlogN),兩次遍歷數組O(n2),雙指針遍歷O(n),總體O(NlogN)+O(n2)∗O(n),O(n3)
空間複雜度:O(1)
作者:zhu_shi_fu
鏈接:https://leetcode-cn.com/problems/4sum/solution/gu-ding-tao-lu-jian-dan-qing-xi-pai-xu-shuang-zhi-/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
int nsize = nums.size();
if (nsize < 4)
return {};
sort(nums.begin(),nums.end());
vector<vector<int>> retvec;
int l, r;
for (int i=0; i<nsize-3; i++)
{
if(nums[i]+nums[i+1]+nums[i+2]+nums[i+3] > target) break;
if(nums[i]+nums[nsize-3]+nums[nsize-2]+nums[nsize-1] < target) continue;
if(i>0 && nums[i]==nums[i-1]) continue;
for (int j=i+1; j<nsize-2; j++)
{
if(nums[i]+nums[j]+nums[j+1]+nums[j+2] > target) break;
if(nums[i]+nums[j]+nums[nsize-2]+nums[nsize-1] < target) continue;
if(j>i+1 && nums[j]==nums[j-1]) continue;
l = j+1;
r = nsize-1;
while (l<r)
{
int sumn = nums[i] + nums[j] + nums[l] + nums[r];
if (sumn < target)
l++;
else if(sumn > target)
r--;
else
{
retvec.push_back({nums[i], nums[j], nums[l], nums[r]});
while(l<r && nums[l] == nums[++l]);
while(l<r && nums[r] == nums[--r]);
}
}
}
}
return retvec;
}
};
重要的是剪枝條件要考慮周全,才能保證捨去不必要的循環。