从一道算法题的几种解法体会一下算法优化的思想。。。
题目:
楼主看到这题第一时间想到的是暴力法:
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%的提交记录,还有更多的优化方案就不再介绍了,不过欢迎大家留言指出!