LeetCode刷題系列18

給定一個包含 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(Nlog⁡N),兩次遍歷數組O(n2),雙指針遍歷O(n),總體O(Nlog⁡N)+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;
    }
};

重要的是剪枝條件要考慮周全,才能保證捨去不必要的循環。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章