求解最大连续子数组问题
2017-08-04 19:46
309 查看
如下图所示为某公司开发的一款股票的走势图的软件,从图中我们可以看到,股票有涨有落,走势情况一目了然。现在要让你为软件添加一项新的功能:现在要用编程实现求解在某一时间段内,什么时候买入什么时候卖出获得的收益最大?
我们可以很容易的想到一种办法,就是逐个对每个时间点的组合进行计算比较,直到找出总收益最大的那一个组合,总共有n*n中组合,时间复杂度为O(n^2)。
最终的收益是买入价格和卖出价格的差值,为了简化问题,我们可以将价格的变动转化为价格差的变动。作如下规定:
∆Pi = Pi - Pi-1
即,第i天的价格变化量等于第i天的价格减去第i-1天的价格。
那么,我们就将问题转化为:从每个时间点组合的价格变动总和中找出最大的。
如下所示的数组,记录了12天之内每一天价格变动的情况:
那么现在的问题就转换为:寻找价格变动和最大那个组合。组合的数量为(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代码实现:
输出结果:
最大连续子数组为: 15 -2 10 -3 20 总和为:40
算法的时间复杂度为:nlgn ,这种递归式的算法对函数栈(线程栈)有一定的要求,在数据量特别庞大的情况下需要考虑栈的溢出问题。
我们可以很容易的想到一种办法,就是逐个对每个时间点的组合进行计算比较,直到找出总收益最大的那一个组合,总共有n*n中组合,时间复杂度为O(n^2)。
最终的收益是买入价格和卖出价格的差值,为了简化问题,我们可以将价格的变动转化为价格差的变动。作如下规定:
∆Pi = Pi - Pi-1
即,第i天的价格变化量等于第i天的价格减去第i-1天的价格。
那么,我们就将问题转化为:从每个时间点组合的价格变动总和中找出最大的。
如下所示的数组,记录了12天之内每一天价格变动的情况:
那么现在的问题就转换为:寻找价格变动和最大那个组合。组合的数量为(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 右 4000 边界,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 ,这种递归式的算法对函数栈(线程栈)有一定的要求,在数据量特别庞大的情况下需要考虑栈的溢出问题。
相关文章推荐
- 动态规划求解连续子数组最大和问题(应该是新的描述方法?)
- [算法导论]练习4.1-5最大连续子数组问题
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- 最大子数组求解问题(算法导论)
- 一维数组和二维数组的最大连续子数组问题
- 最大连续子数组问题-homework-01
- 最大连续子数组和问题
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- 求解最大连续子数组的算法
- 【算法学习】最大子数组问题的分治法求解
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- 第四章 分治策略 4.1 最大子数组问题 (暴力求解算法)
- 求连续子数组的最大和问题
- 分治法求解最大子数组问题
- 最大子数组问题-暴力求解-c++代码实现及运行实例结果
- 分治法求解最大子数组问题
- golang动态规划求解最大连续子数组和
- 求解连续子数组乘积的最大值
- 动态规划--求最大连续子数组的和(Python实现)&求解最大连续乘积字串(Python实现)
- C语言进阶-第6讲:递归法问题求解(递归求数组的最大值)