连续子数组最大和问题
来源:互联网 发布:治痘痘最好的方法知乎 编辑:程序博客网 时间:2024/05/16 07:40
1. 问题描述
输入一个整形数组,求数组中连续的子数组使其和最大。比如,数组x
应该返回 x[2..6]的和187.
2. 问题解决
我们很自然地能想到穷举的办法,穷举所有的子数组的之和,找出最大值。
穷举法
i, j的for循环表示x[i..j],k的for循环用来计算x[i..j]之和。
maxsofar = 0for i = [0, n) for j = [i, n) sum = 0 for k = [i, j] sum += x[k] /* sum is sum of x[i..j] */ maxsofar = max(maxsofar, sum)
有三层循环,穷举法的时间复杂度为
对穷举法的改进1
我们注意到x[i..j]之和 = x[i..j-1]之和 + x[j]
,因此在j的for循环中,可直接求出sum。
maxsofar = 0for i = [0, n) sum = 0 for j = [i, n) sum += x[j] /* sum is sum of x[i..j] */ maxsofar = max(maxsofar, sum)
显然,改进之后的时间复杂度变为
对穷举法的改进2
在计算fibonacci数时,应该还有印象:用一个累加数组(cumulative array)记录前面n-1次之和,计算当前时只需加上n即可。同样地,我们用累加数组cumarr记录:cumarr[i] = x[0] + . . . +x[i]
,那么x [i.. j]之和 = cumarr[j] -cumarr[i - 1]
。
cumarr[-1] = 0for i = [0, n) cumarr[i] = cumarr[i-1] + x[i] maxsofar = 0for i = [0, n) for j = [i, n) sum = cumarr[j] - cumarr[i-1] /* sum is sum of x[i..j] */ maxsofar = max(maxsofar, sum)
时间复杂度依然为
分治法
所谓分治法,是指将一个问题分解为两个子问题,然后分而解决之。具体步骤如下:
先将数组分为两个等长的子数组a, b;
分别求出两个数组a,b的连续子数组之和;
还有一种情况比较容易忽略:有可能最大和的子数组跨越两个数组;
最后比较
ma ma,mb mb,mc mc,取最大即可。
在计算从中间元素开始往左累加的最大值 + 从中间元素开始往右累加的最大值
。
float maxsum3(l, u) if (l > u) /* zero elements */ return 0 if (l == u) /* one element */ return max(0, x[l]) m = (l + u) / 2 /* find max crossing to left */ lmax = sum = 0 for (i = m; i >= l; i--) sum += x[i] lmax = max(lmax, sum) /* find max crossing to right */ rmax = sum = 0 for i = (m, u] sum += x[i] rmax = max(rmax, sum) return max(lmax+rmax, maxsum3(l, m), maxsum3(m+1, u));
容易证明,时间复杂度为
Kadane算法
Kadane算法又被称为扫描法,该算法用到了一个启发式规则:如果前面一段连续子数组的和小于0,那么就丢弃它。其实也蛮好理解的,举个简单例子,比如:数组-1, 2, 3
,-1为负数,为了使得子数组之和最大,显然不应当把-1计入进内。
max_ending_here记录前面一段连续子数组之和。
Initialize: max_so_far = 0 max_ending_here = 0Loop for each element of the array (a) max_ending_here = max_ending_here + x[i] (b) if(max_ending_here < 0) max_ending_here = 0 (c) if(max_so_far < max_ending_here) max_so_far = max_ending_herereturn max_so_far
只遍历了一遍数组,因此时间复杂度为
变种:和不超过k的最大子序列
int best_cumulative_sum(vector<int>&ar, int K){ int N = ar.size(); set<int> cumset; cumset.insert(0); int best = 0, cum = 0; for (int i = 0; i<N; i++) { cum += ar[i]; set<int>::iterator sit = cumset.upper_bound(cum - K); if (sit != cumset.end()) best = max(best, cum - *sit); cumset.insert(cum); } return best;}
3. 参考资料
[1] Jon Bentley, Programming Pearls.
[2] GeeksforGeeks, Largest Sum Contiguous Subarray.
- 最大连续子数组和问题
- 连续子数组最大和问题
- 连续子数组最大和问题
- 连续子数组最大和问题
- 连续子数组最大和问题
- 最大连续子数组问题
- 最大子数组(最大连续区间和)问题
- 连续子数组最大和
- 连续最大子数组和
- 连续子数组最大和
- 最大连续子数组和
- 连续子数组最大和
- 最大连续子数组和
- 最大连续子数组和
- 最大连续子数组和
- 最大连续子数组和
- 最大连续子数组和
- 最大连续子数组和
- 2.zxj小学
- 暑假培训
- HBase官方文档 0.97
- 类别(Category)的作用(二)---对私有方法的前向引用
- zTree插件入门
- 连续子数组最大和问题
- 面试准备之--二叉树的递归与非递归方式
- Android仿天天果园Splash
- Html.DropDownList()的用法
- 高并发数据结构Disruptor解析(5)
- POJ 3461 Oulipo(KMP模板)
- 赛码网_在线编程_军训队列
- 写点什么好呢2? 钱、事业、婚姻、人生意义
- bzoj1433(网络流)