热门搜索 :
考研考公
您的当前位置:首页正文

求解最大连续子数组问题

来源:东饰资讯网

如下图所示为某公司开发的一款股票的走势图的软件,从图中我们可以看到,股票有涨有落,走势情况一目了然。现在要让你为软件添加一项新的功能:现在要用编程实现求解在某一时间段内,什么时候买入什么时候卖出获得的收益最大?

20170816103701390.png

我们可以很容易的想到一种办法,就是逐个对每个时间点的组合进行计算比较,直到找出总收益最大的那一个组合,总共有n*n中组合,时间复杂度为O(n^2)。

最终的收益是买入价格和卖出价格的差值,为了简化问题,我们可以将价格的变动转化为价格差的变动。作如下规定:

∆Pi = Pi - Pi-1

即,第i天的价格变化量等于第i天的价格减去第i-1天的价格。

那么,我们就将问题转化为:从每个时间点组合的价格变动总和中找出最大的。

如下所示的数组,记录了12天之内每一天价格变动的情况:

20170816103717612.png

那么现在的问题就转换为:寻找价格变动和最大那个组合。组合的数量为(n-1)*(n-1),时间复杂度仍旧为O(n^2)。

下面我们采用分治的思想进行求解。将当前的数组Arr[low...high]划分为两个相等规模的数组Arr[low...mid]和Arr[mid+1...high],那么我们要求解的最大连续子数组A[i...j]就存在于Arr[low...mid]、Arr[mid+1...high]以及Arr[low...high]中。也即是一下三种情况:

(1)Arr[low...mid]:low ≦ i ≦ j ≦ mid

(2)Arr[mid+1...high]:mid+1 ≦ i ≦ j ≦ high

(3)Arr[low...high]:low ≦ i ≦ j ≦ high

对于(1)和(2)两种情况,我们可以采用同样的算法,继续将其划分两个相等规模的数组,然后对三种情况进行处理。划分到什么时候停止呢?划分到数组中只有一个元素时,就无法进行划分,这时候就可以停止了。

这样一来,重复性就出现了,也就是说划分的算法是相同的,对于第(3)中情况,求解最大子数组的算法也是一样的:从中间向两边进行查找。下面用c代码实现:

/** 
 * findMaxSubArray 
 * @author liebert 
* @param int arr[] 待查找的数组 
* @param int low 数组的左边界 
* @param int low 数组的右边界 
* @param int result[] 结果数组 0 左边界,1 右边界,2 总和 
* @return void 
*/  
void findMaxSubArray (int arr[], int low, int high, int result[])  
{  
    int i, j, sum, left,leftSum, right, rightSum, mid, crossSum;  
    int resultLeft[3], resultRight[3], resultCross[3];  
  
    if ( low == high ) {  
        result[0] = low;  
        result[1] = high;  
        result[2] = arr[low];  
} else {  
    // 以中点作为分割点  
    mid = (low + high) / 2;  
    // 递归左边子数组  
    findMaxSubArray (arr, low, mid, resultLeft);  
    // 递归右边子数组  
    findMaxSubArray (arr, mid+1, high, resultRight);  
// 计算左边数组最大和 索引  
    sum = 0; leftSum = 0;  
    for (i = mid; i > low; i--) {  
        sum += arr[i];  
        if ( sum > leftSum ) {  
            leftSum = sum;  
            left = i;  
        }  
}  
// 计算右边数组最大和 索引  
    sum = 0; rightSum = 0;  
for (j = mid+1; j < high; j++) {  
    sum += arr[j];  
        if ( sum > rightSum) {  
            rightSum = sum;  
            right = j;  
        }  
}  
// 得到总和  
crossSum = leftSum + rightSum;  
      
    // 比较选择最大子数组  
    if (resultLeft[2] > resultRight[2] && resultLeft[2] > crossSum) {  
        result[0] = resultLeft[0];  
        result[1] = resultLeft[1];  
        result[2] = resultLeft[2];  
} else if (resultRight[2] > resultLeft[2] && resultRight [2] > crossSum){  
        result[0] = resultRight [0];  
        result[1] = resultRight [1];  
        result[2] = resultRight [2];  
} else {  
        result[0] = left;  
        result[1] = right;  
        result[2] = crossSum;  
}  
}  
  
}  
  
int main()  
{  
    int i;  
    int arr[11] = {10,-5,-12,15,-2,10,-3,20,-2,-4,2};  
    int res[3];  
    findMaxSubArray(arr, 0, 10, res);  
      
    printf("最大连续子数组为: ");  
    for(i = res[0]; i <= res[1]; i++){  
        printf("%d ", arr[i]);  
    }  
    printf("总和为:%d", res[2]);  
}  

输出结果:
最大连续子数组为: 15 -2 10 -3 20 总和为:40
算法的时间复杂度为:nlgn ,这种递归式的算法对函数栈(线程栈)有一定的要求,在数据量特别庞大的情况下需要考虑栈的溢出问题。

Top