问题

给定整数: A1,A2,…,An,
求∑jk=iAk 的最大值(为方便起见,假设全部的整数均为负数,则最大子序列和为0)

比如

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

分析

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

解法一:穷举遍历

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//计算并返回所最大子序列的和:穷举遍历
int maxSubSum1(const vector<int> & 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循环就可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//计算并返回所最大子序列的和:穷举优化
int maxSubSum2(const vector<int> & 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)。

解法三:分而治之

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

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

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

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

对于前两种能够递归求解,对于第三种,能够把左右两个部分的和分别求出,然后加在一起。
详细的代码例如以下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
//计算并返回所最大子序列的和:分而治之
int maxSubSum3(const vector<int> & 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)。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//计算并返回所最大子序列的和:最优算法
int maxSubSum4(const vector<int> & 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

从表中能够看出,在数据量非常小的时候(在它小时候=.=)。算法在瞬间就完毕了。差距也不是非常明显。
可是随着输入量的增大,一些算法的弊端逐渐显现出来,效率低的甚至在有限的时间里算不出结果来。
对于非常多高效的算法来说,读取数据往往是解决这个问题的瓶颈,

[C++]四种方式求解最大子序列求和问题的更多相关文章

  1. C#批量插入数据到Sqlserver中的四种方式

    我的新书ASP.NET MVC企业级实战预计明年2月份出版,感谢大家关注! 本篇,我将来讲解一下在Sqlserver中批量插入数据. 先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的 ...

  2. 【Java EE 学习 80 下】【调用WebService服务的四种方式】【WebService中的注解】

    不考虑第三方框架,如果只使用JDK提供的API,那么可以使用三种方式调用WebService服务:另外还可以使用Ajax调用WebService服务. 预备工作:开启WebService服务,使用jd ...

  3. ASP.NET MVC之下拉框绑定四种方式(十)

    前言 上两节我们讲了文件上传的问题,关于这个上传的问题还未结束,我也在花时间做做分割大文件处理以及显示进度的问题,到时完成的话再发表,为了不耽误学习MVC其他内容的计划,我们今天开始好好讲讲关于MVC ...

  4. SWT组件添加事件的四种方式

    在我们CS日常开发过程中会经常去为组件添加事件,我们常用的为AWT与SWT.SWT的事件模型是和标准的AWT基本一样的.下面将按照事件的四种写法来实现它. 一.匿名内部类的写法 new MouseAd ...

  5. Java实现文件复制的四种方式

    背景:有很多的Java初学者对于文件复制的操作总是搞不懂,下面我将用4中方式实现指定文件的复制. 实现方式一:使用FileInputStream/FileOutputStream字节流进行文件的复制操 ...

  6. C#_批量插入数据到Sqlserver中的四种方式

    先创建一个用来测试的数据库和表,为了让插入数据更快,表中主键采用的是GUID,表中没有创建任何索引.GUID必然是比自增长要快的,因为你生成一个GUID算法所花的时间肯定比你从数据表中重新查询上一条记 ...

  7. java 20 -10 字节流四种方式复制mp3文件,测试效率

    电脑太渣,好慢..反正速率是: 高效字节流一次读写一个字节数组 > 基本字节流一次读写一个字节数组 > 高效字节流一次读写一个字节 > 基本字节流一次读写一个字节 前两个远远快过后面 ...

  8. .NET MVC控制器向视图传递数据的四种方式

    .NET MVC控制器向视图传递数据的四种方式: 1.ViewBag  ViewBag.Mvc="mvc"; 2.ViewData ViewBag["Mvc"] ...

  9. JavaScript表单提交四种方式

    总结JavaScript表单提交四种方式 <!DOCTYPE html> <html> <head> <title>JavaScript表单提交四种方式 ...

随机推荐

  1. hibernate学习(一)

    一.准备工作:导入jar包 1.hibernate的jar包 位置: hibernate-release-5.0.2.Final\hibernate-release-5.0.2.Final\lib\r ...

  2. 【Java&Android开源库代码剖析】のAndroid-Universal-Image-Loader-part1

    做Android app开发的同学应该都听说过或者用过nostra13的Android-Universal-Image-Loader开源库,它在图片异步加载.缓存和显示等方面提供了强大灵活的框架.之前 ...

  3. spark的action和transformations汇集

    汇总了Spark支持的Transformations 和Actions 用于备忘! 參考 http://spark.apache.org/docs/latest/programming-guide.h ...

  4. Android最方便的数据库--LitePal

    郭最近看到神分析LitePal相框,我感觉很强烈,尝试了一下,真的好,我不知道,如果你不习惯学习,那么各不相同,我觉得很合适 看完之后,思想,对于我来说,,实体到set颂值,如果数据非常多,那么你可以 ...

  5. POJ 3589 Number-guessing Game(简单题)

    [题目简述]:两个四位数,假设后一个数中的某个数与前一个相应的数的位置和值都相等.则统计数目由几个这种数.记为count1吧. 假设后一个数中的某个数与前一个数的数值相等,但位置不同. 此时这种数的个 ...

  6. Codeforces Round #246 (Div. 2) D. Prefixes and Suffixes(后缀数组orKMP)

    D. Prefixes and Suffixes time limit per test 1 second memory limit per test 256 megabytes input stan ...

  7. MyReport演示下载连接和相关文章索引

    演示地址 Flex集成方式 (旧版2.6) HTML集成方式 MyReport产品站点 NEW 相关文章 ------------------------2.6下面版本号--------------- ...

  8. Java学习文件夹

    每天进步一点点,先研究一门语言深入研究下去.

  9. sha1加密java代码

    sha1 加密 java代码 public static String getSha1(String str){ if(str==null||str.length()==0){ return null ...

  10. 怎样在C++中获得完整的类型名称

    Wrote by mutouyun. (http://darkc.at/cxx-get-the-name-of-the-given-type/) 地球人都知道C++里有一个typeid操作符能够用来获 ...