算法:最大子序列求和问题
最大子序列求和是指给定一组序列,求所有连续子序列的和中的最大值,例如给定数列:
[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);}
算法:最大子序列求和问题相关推荐
- 数据结构与算法--分治算法-最大子序列和问题
分治算法 用于设计算法的一种常用技巧–分治算法(divide and conquer).分治算法由两部分组成: 分(divide):递归然后借机较小的问题(基础情况除外) 治(conquer):然后从 ...
- 最大子序列求和_最大子序列和问题 一步一步到最优
在<数据结构和算法分析 C++描述>上看到了一个例子.看过之后,我就在想,这是怎么一步一步的递推出来的,想了好长时间,才整理成这篇博文. 问题描述: 给定一个整数序列,a0, a1, a2 ...
- 最大子序列求和_最大子序列和问题
问题描述: 给定一个整数序列,a0, a1, a2, -- , an(项可以为负数),求其中最大的子序列和.如果所有整数都是负数,那么最大子序列和为0: 例如:对于序列-2, 11, -4, 13, ...
- 深入浅出学算法007-统计求和
4006: 深入浅出学算法007-统计求和 Time Limit: 1 Sec Memory Limit: 64 MB Submit: 4335 Solved: 2014 Description 求含 ...
- 最大子序列求和_算法——求最大子段和
一.问题描述 给定由n个整数组成的序列(a_1,a_2,-,a_n),最大子段和问题要求该序列形如 的最大值(1≤i≤j≤n),当序列中所有整数均为负整数时,其最大子段和为0. 例如,序列(-20, ...
- 最大子序列求和_连续子序列最大和与乘积问题的分析
问题描述 给定(可能是负的)整数序列A1, A2,...,AN, 寻找(并标识)使Sum(Ak)(k >=i, k <= j)的值最大的序列.如果所有的整数都是负的,那么连续子序列的最大和 ...
- 最大子序列求和_最大连续子序列和
题外话:本文原文是之前在知乎专栏写的一篇文章,现在读来发现好多地方没有写清楚,所以现在做了些修改,希望把分析过程说的更明白一些.为什么要发布到慕课网来呢,主要是因为个人认为这里的IT氛围更好更专业. ...
- 算法题目——子序列和问题(poj-3061)(尺取法)
题目链接:POJ-3061 题意:给定一个序列,使得其和大于或等于S,求最短的子序列长度. 问题分析: 1.首先序列都是正整数,当子序列和大于等于S时,已经没有必要再将右端点继续向右移动.因为再向右移 ...
- 2019 蓝桥杯省赛 B 组模拟赛(一) J. 程序设计:蒜厂年会 环形连续子序列求和问题
题目描述 在蒜厂年会上有一个抽奖,在一个环形的桌子上,有 n 个纸团,每个纸团上写一个数字,表示你可以获得多少蒜币.但是这个游戏比较坑,里面竟然有负数,表示你要支付多少蒜币.因为这些数字都是可见的,所 ...
最新文章
- 微软某员工后悔跳槽阿里:工资才多20万不到,天天加班快崩溃!
- 2018全球100个最有价值的科技品牌 18个中国品牌上榜
- 【面试】迄今为止把同步/异步/阻塞/非阻塞/BIO/NIO/AIO讲的这么清楚的好文章(快快珍藏)...
- python路径找类并获取静态字段
- Python 项目依赖包 第三方库 生成requirements.txt的两种方法
- linux 防火墙 iptables 允许 某个 某段 IP访问 某个端口
- SQL 2005安装问题解决办法
- 物联网第一台设备是烤面包机?
- 计算机复制粘贴教案,信息技术《文本的复制与移动》教案
- 如何在Windows上安装Angular:Angular CLI,Node.js和构建工具指南
- pythonfor循环加2_初中生教你编程---python(for循环)part 2
- Vue(八)发送跨域请求
- mysql 5.5.35 单机多实例配置详解_基于mysql-5.5.32的单机多实例多配置文件的
- next.js 安装简易教程
- Android持久化保存cookie
- springboot异常
- 大数据知识点汇总---Redis,Spark,Kafka,Hive,Mysql,Hbase,Hadoop...
- mysql 个版本区别_MySQL版本区别及选择
- 攻防世界RE练习区题目总结(1-10)
- Win10 使用黑屏重置键 解决 黑屏问题
热门文章
- leetcode 70. 爬楼梯问题(多种方法总结)
- Windows 10安装Adobe Premiere Pro 2020
- centos怎么汉化linux,linux centos 系统中文模式设置方法
- ajax抓取bilibili弹幕,B站( Bilibili)研究会之爬虫数据API获取
- excel 自动填充序号
- MAC电脑上使用E-Study的坑
- DevOps2.0的工具集系列之DevOps理念
- overpic宏包插入文字(latex)
- 腾讯云校园计划10元1月云主机
- 用bootstrapTable实现考勤报表的动态生成