Leecode程序题-四数相加II

从一道算法题的几种解法体会一下算法优化的思想。。。

题目:

楼主看到这题第一时间想到的是暴力法:

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        int ans=0;
        for(int i=0; i<A.size(); i++){
            for(int j=0; j<B.size(); j++){
                for(int k=0; k<C.size(); k++){
                    for(int l=0; l<D.size(); l++){
                        if(A[i]+B[j]+C[k]+D[l]==0)
                            ans++;
                    }
                }
            }
        }
        return ans;
    }
};

通过了单个测试用例

但是对于比较大的输入数组,会超过时间限制

暴力法的时间复杂度为O(n^4),该复杂度级别不能通过所有用例,需要找到更快的方法。

楼主首先想到的是利用 排序+二分查找 将时间复杂度降到O(n^3*log(n)),具体是将D数组排序后,用二分查找目标值 0-A[i]-B[j]-C[k] 的上下界,避免遍历D数组。实现如下:

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        int ans=0;
        sort(D.begin(), D.end());
        for(int i=0; i<A.size(); i++){
            for(int j=0; j<B.size(); j++){
                for(int k=0; k<C.size(); k++){
                    int lb=lowborder(D, 0-A[i]-B[j]-C[k]), hb=highborder(D, 0-A[i]-B[j]-C[k]);
                    if(lb!=-1)
                        ans+=hb-lb+1;
                }
            }
        }
        
        return ans;
    }
    
    int highborder(vector<int>& nums, int target){
        int lo=0, hi=nums.size()-1;
        while(lo<=hi){
            int mi=lo+(hi-lo)/2;
            if(nums[mi]<target)
                lo=mi+1;
            else if(nums[mi]>target)
                hi=mi-1;
            else 
                lo=mi+1;
        }
        if(lo-1>=0&&nums[lo-1]==target)//注意加限制lo-1的条件
            return lo-1;
        else 
            return -1;
    }
    int lowborder(vector<int>& nums, int target){
        int lo=0, hi=nums.size()-1;
        while(lo<=hi){
            int mi=lo+(hi-lo)/2;
            if(nums[mi]<target)
                lo=mi+1;
            else if(nums[mi]>target)
                hi=mi-1;
            else
                hi=mi-1;
        }
		if(hi+1<=nums.size()-1&&nums[hi+1]==target)//注意加hi+1的限制条件
            return hi+1;
        else
            return -1;
    }
};

遗憾的是,仍旧无法通过所有测试用例,不过对比暴力法,在允许的测试时间内,这一方法能够测试更多的用例(23<46),用 排序+二分查找 优化产生了效果。

 所以我们需要更快的算法。既然已经知道二分查找可以对算法进行优化,那可不可以尽可能多的使用这一方法呢?我们可以将四数之和转变为(A[i]+B[j])以及(C[k]+D[l]),利用一个n*n大小的数组存储 CD=(C[k]+D[l]),对 CD 进行排序,并在其中查找 -(A[i]+B[j]) 的上下界,得到问题的解。实现如下:

class Solution {
public:
    int fourSumCount(vector<int>& A, vector<int>& B, vector<int>& C, vector<int>& D) {
        int ans=0;
        vector<int> CD;
        for(int k=0; k<C.size(); k++)
            for(int l=0; l<D.size(); l++)
                CD.push_back(C[k]+D[l]);              
        sort(CD.begin(), CD.end());
        for(int i=0; i<A.size(); i++){
            for(int j=0; j<B.size(); j++){
                int lb=lowborder(CD, 0-A[i]-B[j]), hb=highborder(CD, 0-A[i]-B[j]);
                if(lb!=-1)
                    ans+=hb-lb+1;
            }
        }        
        return ans;
    }
    /*二分查找算法与之前的相同,此处省略*/
};

算法时间复杂度来自  构造CD数组O(n^2) + CD排序O(log(n^2)) + CD中查找 -(A[i]+B[j]) O(n^2*log(n^2)) ,其中级别最高的是 O(n^2*log(n^2)) 对比上一优化方法提高了一个线性级别。测试结果:

通过了所有用例,但只超过了58.35%的提交记录,还有更多的优化方案就不再介绍了,不过欢迎大家留言指出!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值