最大子序列求和是指给定一组序列,求所有连续子序列的和中的最大值,例如给定数列:

[5,-2,-5,6]最大子序列和是6;[1, 2, -3, 4, -5, 6, 7, 8, -9, 10]最大子序列和是22;

下面将利用几种不同的算法来解决此问题,重要的是理解不同算法中所代表的思想

1、穷举法

穷举法的思想比较简单,它是指列举所有的可能,来得到问题最终的解;

在此问题中,可以利用穷举法将所有的子序列的和计算出来,来得到最大子序列的和;

假设子序列的起点为i,那么i的范围在数组下标中可以是:[0,arrays.length-1];

针对起点为i的子序列,由于子序列是连续的,那么它的终点的范围是[i,arrays.length-1];

最后我们需要对[i,j]的子序列进行求和,并把结果每次与max比较,以此得到最大子序列和max;

    //穷举法,时间复杂度为O(N^3)public static int method_1(int[] arrays) {int max = 0;//每个子序列的起点 = ifor (int i = 0; i < arrays.length; i++) {//每个子序列的终点 = jfor (int j = i; j < arrays.length; j++) {int sum = 0;//子序列求和for (int k = i; k <= j; k++) {sum = sum + arrays[k];}//当出现子序列和大于max,用sum替换掉maxif (sum > max) {max = sum;}}}return max;}

2、穷举优化

显然,上面算法的时间复杂度O(N^3)并不能让人满意,不过我们可以简单优化一下:

列举所有的子序列[i,j]依然不变,但是针对子序列[i,j]求和,我们完全可以省略这一步,当固定起点i时,以i为起点的子序列的终点j的范围[i,arrays.length-1]是连续的,可以发现,j=i+1为子序列的终点时,它的和为:

SUM(i,j)=SUM(i,i+1)=SUM(i,i)+arrays[i+1];

同理,j=i+2时:

SUM(i,j)=SUM(i,i+2)=SUM(i,i+1)+arrays[i+2]

……

也就是说,我们可以把上一次子序列的求和保存起来,留待下次j递增(j++)后使用,即不用针对每次子序列[i,j],去重新计算它的和,在代码里,只需要把对sum的初始化int sum = 0 提到上一层循环里就可以了,并去掉重复计算的for循环就可以了,此方法的时间复杂度为O(N^2)

    //穷举优化:时间复杂度为O(N^2)public static int method_2(int[] arrays) {int max = 0;//每个子序列的起点 = ifor (int i = 0; i < arrays.length; i++) {int sum = 0;//每个子序列的终点 = jfor (int j = i; j < arrays.length; j++) {sum = sum + arrays[j];//当出现子序列和大于max,用sum替换掉maxif (sum > max) {max = sum;}}}return max;}

这里稍微有点动态规划的思想,但并不完全,下面我们将介绍基于动态规划的思想的Kadane算法对此问题的解法思路

3、Kadane算法-动态规划

        

穷举法是针对具体的子序列[i,j]去求解,虽然做了优化减少了重复计算,但依然需要比较高的时间复杂度。

最大子序列和的最终答案是值,而不用去求具体的子序列,所以这里我们可以巧妙的运用动态规划的思想来解决,动态规划的核心思想是:拆分成若干子问题,记住过往,减少重复计算。

        假设我们求长度为N的序列的最大子序列和,可以拆分成N个子问题来计算,假设此数组序列下标是i,那么这N个子问题分别是:i=[0]、i=[0,1]、i=[0,1,2]、......、i=[0,1,2,...,N-1]的子序列的最大子序列和,我们这里可以不用逆推,直接采用顺推的方式来实现。

我们可以根据i=[0]的子序列的结果,去推算i=[0,1]的结果,然后用i=[0,1]的子序列的结果去推算i=[0,1,2]的结果,以此类推,最终推算出i=[0,1,2,...,N-1]的结果;

然而值得我们注意的是,我们需要知道使用前一个子问题去推算后一个子问题,它们之间的连接关系:

        当 i(k)=[0,1,2,3,...k] --> i(k+1)=[0,1,2,3,...k,k+1],即用i(k)结果去推算i(k+1)的结果的时候:

        我们定义两个变量max,sum;max(k)代表i(k)的结果(即最大子序列和),sum(k)代表

i(k)序列的累加,(max ≠ sum);

                sum(k+1) = sum(k) + arrays[k+1]

                当sum(k+1) > max(k) 时,则 max(k+1) = sum(k+1)

                否则max(k+1)=max(k);

                要使上述成立我们必须所做的一个操作是,当sum < 0时,需要把sum = 0(结合下面i = 4/5时更容易理解)

例如对于数组[1, 2, -3, 4, -5, 6, 7, 8, -9, 10],

Arrays 1 2 -3 4 -5 6 7 8 -9 10
i(下标) 0 1 2 3 4 5 6 7 8 9

详解,当遍历到:

i=0时:

sum=1  ----> sum>max ----> max = 0 : 序列[1]的最大子序列和为 1

i=1时:

sum = 3 ----> sum>max ----> max = 3 : 序列[1,2]的最大子序列和为3

i=2时:

sum=0 ----> sum<max ----> max = 3 : 序列[1,2,-3]的最大子序列和为3

i=3时:

sum=4 ----> sum>max ----> max = 4 : 代表序列[1,2,-3,4]的最大子序列和为4

(注意:此时的和为最大的值的子序列有两个[1,2,-3,4],[4],但我们并不在意,我们需要在意的是状态(sum是否小于0)以及max(sum是否大于max),sum如果一直没有小于0,那么sum的值对于后面的累加都是有效的增大,而sum只要一小于0,我们需要把sum=0,它才不影响后面最大值的累加。强调一下:我们只关注当前所遍历过的这整个一块的最大子序列的和max以及sum值的状态

i=4时:

sum=-1 ----> sum<max ----> max = 4 ---->sum=0:序列[1,2,-3,4,-5]的最大子序列和为4

i=5时:

sum=6 ----> sum>max ----> max = 6 : 序列[1,2,-3,4,-5,6]的最大子序列和为6

......

i=8时:

sum=6+7+8-9 ----> sum<max(6+7+8) ----> max=21 : 序列[1, 2, -3, 4, -5, 6, 7, 8, -9]的最大子序列和为21

i=9时:

sum=6+7+8-9+10=22 ----> sum > max ----> max = 22 :序列[1, 2, -3, 4, -5, 6, 7, 8, -9, 10]的最大子序列和为22。

可以清晰的看到,我们只需要一次遍历,就可以得到最终的解,时间复杂度为O(N);

代码如下:

    //动态规划,时间复杂度O(N)public static int method_3(int[] arrays) {int max = 0;int sum = 0;for (int i = 0; i < arrays.length; i++) {sum = sum + arrays[i];if (sum > max) {max = sum;}else if(sum < 0){sum = 0;}}return max;}

4、分治策略

分治策略的核心思想为:分而治之。

它跟动态规划相同点:都是将问题分成若干个子问题。

不同点:动态规划的场景一般为:每个子问题与前后都是有联系的,以1推2,以2推3,依次推到最终结果;而分治策略的场景一般为:先独立的计算出每个子问题的结果,再合并结果得到最终结果。

左半部分                  起点                    右半部分
1 2 -3 4 -5 6 7 8 -9 10

在此问题中,我们也同样可以使用分治策略来解决:我们很容易知道最大子序列和可能在三处出现,如:

A、左半部分:

B、右半部分:

C、中间部分(左右都占):

AB两种情况我们可以通过递归求解;C情况我们可以算出以左半部分的起点向左依次遍历的最大和值,以右半部分的起点向右依次遍历得出的最大和值,两者相加;最终我们比较ABC三者的最大值,即可返回最终结果。

此种方法的时间复杂度为O(N*logN),想比较动态规划而言,它有些地方重复计算了,但是如果将问题转换为求最大子序列的起点跟终点,动态规划或许不太适用了。

​​​

    //分治策略,时间复杂度为O(N*logN)public static int method_4(int[] arrays,int left,int right) {//基准情况if(left == right){if(arrays[left] > 0){return arrays[left];}else{return 0;}}int center = (left+right)/2;//递归求左半部分(A情况)int maxLeft = method_4(arrays,left,center);//递归求右半部分(B情况)int maxRight = method_4(arrays,center+1,right);//求C情况的以左半部分起点向左遍历的最大值int maxLeftBorder = 0;int sumLeftBorder = 0;for(int i = center;i>=left;i--){sumLeftBorder = sumLeftBorder+arrays[i];if(sumLeftBorder>maxLeftBorder){maxLeftBorder = sumLeftBorder;}}//求C情况的以右半部分起点向右遍历的最大值int maxRightBorder = 0;int sumRightBorder = 0;for(int i = center+1;i<=right;i++){sumRightBorder = sumRightBorder+arrays[i];if(sumRightBorder>maxRightBorder){maxRightBorder = sumRightBorder;}}//返回 maxLeft 、maxRight、maxLeftBorder+maxRightBorder中的最大值int temp = Math.max(maxLeft, maxRight);return Math.max(temp,maxLeftBorder+maxRightBorder);}

算法:最大子序列求和问题相关推荐

  1. 数据结构与算法--分治算法-最大子序列和问题

    分治算法 用于设计算法的一种常用技巧–分治算法(divide and conquer).分治算法由两部分组成: 分(divide):递归然后借机较小的问题(基础情况除外) 治(conquer):然后从 ...

  2. 最大子序列求和_最大子序列和问题  一步一步到最优

    在<数据结构和算法分析 C++描述>上看到了一个例子.看过之后,我就在想,这是怎么一步一步的递推出来的,想了好长时间,才整理成这篇博文. 问题描述: 给定一个整数序列,a0, a1, a2 ...

  3. 最大子序列求和_最大子序列和问题

    问题描述: 给定一个整数序列,a0, a1, a2, -- , an(项可以为负数),求其中最大的子序列和.如果所有整数都是负数,那么最大子序列和为0: 例如:对于序列-2, 11, -4, 13, ...

  4. 深入浅出学算法007-统计求和

    4006: 深入浅出学算法007-统计求和 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 4335 Solved: 2014 Description 求含 ...

  5. 最大子序列求和_算法——求最大子段和

    一.问题描述 给定由n个整数组成的序列(a_1,a_2,-,a_n),最大子段和问题要求该序列形如 的最大值(1≤i≤j≤n),当序列中所有整数均为负整数时,其最大子段和为0. 例如,序列(-20, ...

  6. 最大子序列求和_连续子序列最大和与乘积问题的分析

    问题描述 给定(可能是负的)整数序列A1, A2,...,AN, 寻找(并标识)使Sum(Ak)(k >=i, k <= j)的值最大的序列.如果所有的整数都是负的,那么连续子序列的最大和 ...

  7. 最大子序列求和_最大连续子序列和

    题外话:本文原文是之前在知乎专栏写的一篇文章,现在读来发现好多地方没有写清楚,所以现在做了些修改,希望把分析过程说的更明白一些.为什么要发布到慕课网来呢,主要是因为个人认为这里的IT氛围更好更专业. ...

  8. 算法题目——子序列和问题(poj-3061)(尺取法)

    题目链接:POJ-3061 题意:给定一个序列,使得其和大于或等于S,求最短的子序列长度. 问题分析: 1.首先序列都是正整数,当子序列和大于等于S时,已经没有必要再将右端点继续向右移动.因为再向右移 ...

  9. 2019 蓝桥杯省赛 B 组模拟赛(一) J. 程序设计:蒜厂年会 环形连续子序列求和问题

    题目描述 在蒜厂年会上有一个抽奖,在一个环形的桌子上,有 n 个纸团,每个纸团上写一个数字,表示你可以获得多少蒜币.但是这个游戏比较坑,里面竟然有负数,表示你要支付多少蒜币.因为这些数字都是可见的,所 ...

最新文章

  1. 微软某员工后悔跳槽阿里:工资才多20万不到,天天加班快崩溃!
  2. 2018全球100个最有价值的科技品牌 18个中国品牌上榜
  3. 【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)...
  4. python路径找类并获取静态字段
  5. Python 项目依赖包 第三方库 生成requirements.txt的两种方法
  6. linux 防火墙 iptables 允许 某个 某段 IP访问 某个端口
  7. SQL 2005安装问题解决办法
  8. 物联网第一台设备是烤面包机?
  9. 计算机复制粘贴教案,信息技术《文本的复制与移动》教案
  10. 如何在Windows上安装Angular:Angular CLI,Node.js和构建工具指南
  11. pythonfor循环加2_初中生教你编程---python(for循环)part 2
  12. Vue(八)发送跨域请求
  13. mysql 5.5.35 单机多实例配置详解_基于mysql-5.5.32的单机多实例多配置文件的
  14. next.js 安装简易教程
  15. Android持久化保存cookie
  16. springboot异常
  17. 大数据知识点汇总---Redis,Spark,Kafka,Hive,Mysql,Hbase,Hadoop...
  18. mysql 个版本区别_MySQL版本区别及选择
  19. 攻防世界RE练习区题目总结(1-10)
  20. Win10 使用黑屏重置键 解决 黑屏问题

热门文章

  1. leetcode 70. 爬楼梯问题(多种方法总结)
  2. Windows 10安装Adobe Premiere Pro 2020
  3. centos怎么汉化linux,linux centos 系统中文模式设置方法
  4. ajax抓取bilibili弹幕,B站( Bilibili)研究会之爬虫数据API获取
  5. excel 自动填充序号
  6. MAC电脑上使用E-Study的坑
  7. DevOps2.0的工具集系列之DevOps理念
  8. overpic宏包插入文字(latex)
  9. 腾讯云校园计划10元1月云主机
  10. 用bootstrapTable实现考勤报表的动态生成