Maximum_subarray_problem

leetcode上的题目:Best Time to Buy and Sell Stock

问题很简单,只不过想总结一下其中经典的一些解法。

有不同的O(n)时间复杂度的解法,比如遍历i=[0, n),每次计算nums[i]与[0, i]之间的最小值的差,更新全局最大值。代码如下:

方法一:

// time: O(n),  space: O(1)

int minv = INT_MAX;

int gmax = INT_MIN;

for (int i = 0; i < n; ++i) {

minv = min(nums[i], minv);

gmax = max(gmax, nums[i] - minv);

}

return gmax;


该题也可以转化为一个经典的问题:求一个数组的子数组最大和。

记diff[i] = nums[i] - nums[i - 1]

则 nums[i] - nums[j] = diff[i] + diff[i - 1] + ... + diff[j+1], 所以问题转化为求diff数组的子数组最大和。该问题有一些经典的解法:

方法二:

分支界定,时间复杂度为O(nlog(n))
把数组A[1..n]分成两个相等大小的块:A[1..n/2]和A[n/2+1..n],最大的子数组只可能出现在三种情况:
    A[1..n]的最大子数组和A[1..n/2]最大子数组相同;
    A[1..n]的最大子数组和A[n/2+1..n]最大子数组相同;
    A[1..n]的最大子数组跨过A[1..n/2]和A[n/2+1..n]
前两种情况的求法和整体的求法是一样的,因此递归求得。
第三种,我们可以采取的方法也比较简单,沿着第n/2向左搜索,直到左边界,找到最大的和maxleft,以及沿着第n/2+1向右搜索找到最大和maxright,那么总的最大和就是maxleft+maxright。
而数组A的最大子数组和就是这三种情况中最大的一个。


方法三:

动态规划:time:O(n)

如果用函数f(i)表示以第i个数字结尾的子数组的最大和,那么我们需要求出max(f[0...n])。我们可以给出如下递归公式求f(i)

这个公式的意义:

  1. 当以第(i-1)个数字为结尾的子数组中所有数字的和f(i-1)小于0时,如果把这个负数和第i个数相加,得到的结果反而不第i个数本身还要小,所以这种情况下最大子数组和是第i个数本身。
  2. 如果以第(i-1)个数字为结尾的子数组中所有数字的和f(i-1)大于0,与第i个数累加就得到了以第i个数结尾的子数组中所有数字的和。


方法四:
Kadane's algorithm, time:O(n), space:O(1)
int max_ending_here = 0, max_so_far = 0;
for (auto x : nums) { 
    max_ending_here = max(0, max_ending_here + x);
    max_so_far = max(max_so_far, max_ending_here);
}
参考资料:
http://www.cnblogs.com/xkfz007/archive/2012/05/17/2506299.html
http://www.cnblogs.com/xwdreamer/archive/2012/05/04/2482507.html
https://en.wikipedia.org/wiki/Maximum_subarray_problem


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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值