算法题:数组划分为和最相近2个子数组

算法题:数组划分为和最相近2个子数组

将一个数组划分为2个子数组,要求子数组的和尽可能接近。

思路

  1. 将数组排序,并计算出整个数组和。
  2. 计算区间和。从最小元素开始计算一个连续区间的和,当和小于数组和一半时,区间右边界右移,当和大于数组和一半时,区间左边界右移。当区间和最接近数组和的一半时记录左右边界的位置和区间和。当区间和等于数组一半时,此时区间中的元素即为一个划分方案中的子数组,直接返回。退出条件为区间左边界小于区间右边界,且右边界不超过数组最后一个元素。

分析

从最小的元素开始计算区间和,当和小于中值右边界右移。由于是从最小的元素开始的,可以知道右边界右移后区间后肯定是增加的。当区间和大于中值时,将左边界右移,也就是从区间中剔除一个较小的数,以最小的步长向中值靠拢。

时间复杂度 O ( n l o g n ) 。空间复杂度O(n)。

代码

vector<vector<int> > Leetcode::partitionNearestSumSubArr(const vector<int> &arr)
{
    if(arr.size()<2){
        return vector<vector<int> >({arr,vector<int>()});
    }

    vector<int> sortedArr=arr;
    SortAlgorithmn::quickSort(sortedArr);

    int sum=0;
    for(const auto& i : sortedArr) sum+=i;

    auto lClose=sortedArr.begin();
    auto rOpen=sortedArr.begin()+1;

    auto minDist=std::numeric_limits<int>::max();
    int curSum=*sortedArr.begin();
    for(auto i=sortedArr.begin(),j=sortedArr.begin()+1;i<j && j<=sortedArr.end();){
        int curDist=curSum*2-sum;
        if(abs(curDist)<minDist){
            lClose=i;
            rOpen=j;
            minDist=abs(curDist);
            if(minDist == 0) break;
        }
        if(curDist>0){
            curSum-=(*i);
            ++i;
        }else if(curDist<0){
            curSum+=(*j);
            ++j;
        }else{
            lClose=i;
            rOpen=j;
            break;
        }
    }

    vector<int> first;
    first.assign(lClose,rOpen);
    sortedArr.erase(lClose,rOpen);;

    return vector<vector<int> >({first,sortedArr});
}
排序的函数可以用stl中的算法。
注意左右区间是左闭右开。
对于中值的处理不要用总和除以2,直接用区间和乘以2减总和来表示与中值的距离。

完整代码见github: https://github.com/junbujianwpl/LeetcodePro.git


总结

对于最接近的问题,可以考虑下用夹逼法。当然夹逼法是需要排序的。

对于不连续的区间,如头加尾,可以求其互补区间

猜你喜欢

转载自blog.csdn.net/junbujianwpl/article/details/80220308