博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[C++]四种方式求解最大子序列求和问题
阅读量:6186 次
发布时间:2019-06-21

本文共 3255 字,大约阅读时间需要 10 分钟。

问题

给定整数: A1,A2,,An

jk=iAk 的最大值(为方便起见,假设全部的整数均为负数,则最大子序列和为0)

比如

对于输入:-2,11,-4,13,-5,-2,答案为20,即从A2A4

分析

这个问题之所以有意思。是由于存在非常多求解它的算法。

解法一:穷举遍历

老老实实的穷举出全部的可能,代码例如以下:

1234567891011121314151617181920212223242526272829
//计算并返回所最大子序列的和:穷举遍历int maxSubSum1(const vector
& a){ //用来存储最大子序列的和 int maxSum = 0; //i标记子序列的头 for (int i = 0; i < a.size(); i++) { //j标记子序列的尾 for (int j = i; j < a.size(); j++) { //用来存储当前头尾计算的求和结果 int thisSum = 0; //将子序列的值依次增加求和结果 for (int k = i; k <= j; k++) { thisSum += a[k]; } //存储两者的最大值 if(thisSum > maxSum) maxSum = thisSum; } } return maxSum;}

这是大多数人都会想到的方法:把全部的可能都列举出来,然后再把子序列的全部值都加起来求和。

简单粗暴的攻克了问题。并且还非常好理解。

这样的算法的时间复杂度为O(N3)

解法二:穷举优化

事实上第三个for循环全然没有必要,在第二层for循环的时候就计算求得的和并且继续带入下一轮的for循环就可以:

123456789101112131415161718192021222324252627
//计算并返回所最大子序列的和:穷举优化int maxSubSum2(const vector
& a){ //用来存储最大子序列的和 int maxSum = 0; //i标记子序列的头 for (int i = 0; i < a.size(); i++) { //用来存储当前头尾计算的求和结果 int thisSum = 0; //j标记子序列的尾 for (int j = i; j < a.size(); j++) { //将子序列的值增加上次求和结果 thisSum += a[j]; //存储两者的最大值 if(thisSum > maxSum) maxSum = thisSum; } } return maxSum;}

这样的算法的时间复杂度为O(N2)

解法三:分而治之

分而治之,顾名思义分为两个部分

  • 分:把大问题分成大致相等的两个子问题,然后递归的进行求解。
  • 治:把两个子问题的解合并到一起并再做少量的附加工作。

在最大子序列和的问题里,最大子序列的和可能出如今三个地方:

  • 整个出如今输入数据的左半部
  • 整个输入数据的右半部
  • 横跨左右两个部分

对于前两种能够递归求解,对于第三种,能够把左右两个部分的和分别求出,然后加在一起。

详细的代码例如以下:

123456789101112131415161718192021222324252627282930313233343536373839404142434445
//计算并返回所最大子序列的和:分而治之int maxSubSum3(const vector
& a,int left,int right){ //基础情况:单个元素。直接返回这个数值或者0 if(left == right) { return a[left]; } //获取中点 int center = (left + right) / 2; /* 整个出如今输入数据的左半部的最大子序列求和 */ int leftMaxSum = maxSubSum3(a,left,center); /* 整个出如今输入数据的右半部的最大子序列求和 */ int rightMaxSum = maxSubSum3(a,center+1,right); //计算左右两个子序列求和结果的最大值 int lrMaxSum = max(leftMaxSum,rightMaxSum); /* 横跨左右两个部分的最大子序列求和 */ //从center向左处理左半边 int maxLeftSum = 0; int leftSum = 0; for (int i = center; i >= left; i--) { leftSum += a[i]; maxLeftSum = max(maxLeftSum,leftSum); } //从center向右处理右半边 int maxRightSum = 0; int rightSum = 0; for (int j = center+1; j <= right; j++) { rightSum += a[j]; maxRightSum = max(maxRightSum,rightSum); } //返回求和和前面算出结果的最大值 return max( lrMaxSum, maxLeftSum+maxRightSum);}

这样的算法的时间复杂度为O(NlogN)

解法四:联机算法

先来解释一下联机算法的概念:

联机算法:在随意时刻,算法对要操作的数据仅仅读入(扫描)一次,一旦被读入并处理,它就不须要在被记忆了。而在此处理过程中算法能对它已经读入的数据马上给出对应子序列问题的正确答案。

具有这样的特性的算法叫做联机算法(on-line algorithm)。

对于这个问题。代码例如以下:

1234567891011121314151617181920212223
//计算并返回所最大子序列的和:最优算法int maxSubSum4(const vector
& a){ //终于结果 int maxSum = 0; //当前求和 int nowSum = 0; //遍历序列的全部元素 for (int j = 0; j < a.size(); j++) { //将当前元素增加到结果中 nowSum += a[j]; //假设大于最大值,则存储为新的最大值 if(nowSum > maxSum) maxSum = nowSum; else if(nowSum < 0) nowSum = 0; } return maxSum;}

这样的算法的时间复杂度为O(N)

总结

下表是统计的四种算法的运算结果:

输入大小 O(N3) O(N2) O(NlogN) O(N)
N=10 0.000009 0.000004 0.000006 0.000003
N=100 0.002580 0.000109 0.000045 0.000006
N=1000 2.281013 0.010203 0.000485 0.000031
N=10000 NA 1.2329 0.005712 0.000317
N=100000 NA 135 0.064618 0.003206

从表中能够看出,在数据量非常小的时候(在它小时候=.=)。算法在瞬间就完毕了。差距也不是非常明显。

可是随着输入量的增大,一些算法的弊端逐渐显现出来,效率低的甚至在有限的时间里算不出结果来。
对于非常多高效的算法来说,读取数据往往是解决这个问题的瓶颈,

转载地址:http://mgada.baihongyu.com/

你可能感兴趣的文章
ORACLE_OCM.MGMT_DB_LL_METRICS报错
查看>>
文件服务器的配置与管理(3) 共享文件夹的创建与使用
查看>>
Windows快捷方式
查看>>
Essential Linux Device Driver附录A . Linux汇编
查看>>
eclipse导入的工程莫名报错误
查看>>
[Android]基于RxJava、RxAndroid的EventBus实现
查看>>
细说Linq之Aggregate
查看>>
Gradle 提速:每天为你省下一杯喝咖啡的时间
查看>>
《iOS 核心动画高级技巧》笔记
查看>>
前端小知识10点(2019.5.18)
查看>>
Tensorflow minist-softmax
查看>>
Kotlin中的also、let、run、with、apply函数的用法
查看>>
常用 Markdown 语法汇总
查看>>
12、Flutter Widget - InheritedModel;
查看>>
VR全景创业:这些创业条件你具备了吗?
查看>>
WEB前端学习如何分清主次和优先级?
查看>>
小程序·云开发——正在悄悄改变小程序开发的模式
查看>>
运行期间抛出NoSuchMethodError模拟及原因分析
查看>>
基于Spring Boot2 + Spring Security OAuth2 实现单点登陆(一)
查看>>
跟我一起来用C++写web服务器吧(二)
查看>>