问题——
给定N个整数(有可能是负数)A1,A2,A3,A4...An,求最大子序列和。
(子序列必须是连续的);比如,对于输入,-2,11,-4,13,-5,-2;这个序列,
答案是20,即从A2到A4。
对于这个问题,你怎么想的呢?下面有四种解法,看看你的解法是不是其中之一。
解法一、穷举
解题思路——
既然是求某一个连续的子序列的最大和,那么我们把所有的子序列的和都加一遍,然后用一个变量来存储最大的和值,当遍历一遍所有子序列,即可得到最大的和。由于这个子序列长度可以是1,也可以是N,因此需要三重循环。
具体程序——
private static int calOne(int[] list){ int maxSum = 0; for (int i = 0; i < list.length; i++) { for (int j = i; j < list.length; j++) { int thisSum = 0; for (int k = i; k < j; k++) { thisSum += list[k]; } if (thisSum > maxSum) { maxSum = thisSum; } } } return maxSum; }
测试——
为了测试其性能,构建大量数据比较耗费时间,所以这里我让它重复计算某一个数组1万次。
public static void main(String[] args) { int[] test = {-2,11,-4,13,-5,-219,23,234,-190,280,-20,1340,123,-324,43,-35}; long time = System.currentTimeMillis(); for (int i = 0; i < 9999; i++) { calOne(test); } System.out.println(calOne(test)); System.out.println("算法1所用时间:"+(System.currentTimeMillis()-time)+"毫秒"); }
结果是:
1790
算法1所用时间:22毫秒
明显,三重循环,其运行时间是O(N^3);
解法二、改进的穷举
通过观察,我们发现,其实第三重循环是不必要的,因为第二次循环已经计算了第三次循环的数值,因此可以撤销。
private static int calTwo(int[] list) { int maxSum = 0; for (int i = 0; i < list.length; i++) { int thisSum = 0; for (int j = i; j < list.length; j++) { thisSum += list[j]; if (thisSum > maxSum) { maxSum = thisSum; } } } return maxSum; }
撤销后的时间复制度是O(N^2),我们来看运行时间。
1790
算法2所用时间:6毫秒
确实减少了不少时间,但是这还是很差劲的穷举算法,需要改进。
解法三、联机算法
联机算法优缺点——
在任意时刻,算法对要操作的数据只读入(扫描)一次,一旦被读入并处理,它就不需要在被记忆了。而在此处理过程中算法能对它已经读入的数据立即给出相应子序列问题的正确答案。具有这种特性的算法叫做联机算法(on-line algorithm)。
该算法仅需要常量空间并以线性时间运行,因此联机算法几乎是完美的算法。
优点:占用空间少,所用时间少
缺点:不宜设计,正确性不易观察,同时附加保留信息较少
具体程序——
private static int calThree(int[] list) { int maxSum = 0,thisSum = 0;; for (int j = 0; j < list.length; j++) { thisSum += list[j]; if (thisSum > maxSum) { maxSum = thisSum; }else if(thisSum <0){ thisSum = 0; } } return maxSum; }
可以看到,联机算法的时间复杂度是O(N),节省了很多时间。运行验证下。
同样地计算一组数据1万次,结果是——
1790
算法3所用时间:2毫秒
关于这个问题,还有一种分治策略的算法,虽然不如联机算法,但是比起差劲的穷举,还是改进不少的。
以上是个人的读书笔记,书籍为《数据结构与算法描述》。欢迎探讨。
算法笔记1-最大子序列和问题的求解,布布扣,bubuko.com
原文地址:http://blog.csdn.net/linfeng24/article/details/33023471