问题

给定整数: 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. listview改变选中行字体颜色

    [android]listview改变选中行字体颜色 目标:选中item,其字体设置为#3197FF,未选中的,其字体为#FFFFFF 与listvew设置选中行item背景图片一样,使用select ...

  2. 求第i个小的元素 时间复杂度O(n)

    #include<iostream> //求第i个小的元素 时间复杂度O(n) #include<cstdlib> #include<ctime> using na ...

  3. C++ 多态性分析

    编译 - 时间多态性--函数重载 编译后的中间代码(例如GCC产生.o文件.此时还不是汇编语言)函数名字有变化,看以下两个样例. void cc_show(const char*str)     -& ...

  4. POJ--3268--Silver Cow Party【SPFA+邻接表】

    题意:一些牛要去某一点參加聚会,然后再回到自己家,路是单向的,问花费时间最多的那头牛最少须要花费多长时间. 思路:从聚会地点返回,相当于是从某一点到其它各个点的最短路径.从牛的家中走到聚会地点,能够把 ...

  5. JavaScript 中的闭包和作用域链(读书笔记)

    要想理解闭包,应当先理解JavaScript的作用域和作用域链. JavaScript有一个特性被称之为“声明提前(hoisting)”,即JavaScript函数里声明的所有变量(但不涉及赋值)都被 ...

  6. birkenfeld / sphinx-contrib — Bitbucket

    birkenfeld / sphinx-contrib — Bitbucket README for sphinx-contrib This repository contains a collect ...

  7. java,spring,tomcat,跨域设置

    通过工具远程访问rest会出现  已阻止跨源请求:同源策略禁止读取位于 http://XXXXX:1200/gyly/busData 的远程资源.(原因:CORS 请求失败).  修改方法 添加Fil ...

  8. sql server 行列互转

    1 列转行 测试脚本 ),课程 ),分数 int) ) ) ) ) ) ) go 转化脚本 select 姓名 , end) 语文, end) 数学 , end) 物理 from tb group b ...

  9. Jquery 对话框确认

    $("#aa").click(function(){ if(confirm("是否继续")){ $(#aa).fadeOut(500); } })

  10. Windows下Putty连接虚拟机Ubuntu

    本文的题目是Windows下使用Putty连接虚拟机中的Ubuntu. 事实上针对这种一个问题,已经有非常多的文章.blog能够參考和学习.可是在本人的学习过程中还是遇到可非常多的问题. 特写下自己的 ...