今天的软件工程课老师让我们结对的小组现场来完成一个小程序。
题目: 输入一个整型数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。
我和永哥随即进入紧张的讨论,和大多数同学一样,很短的时间内谁也没有顾及时间复杂度的问题,首先要对每相邻的数组进行求和,然后再将求和的结果与max最大值进行比较,若大于则互换继续比较。在求和的问题上,我们将每个元素顺序跟后面相邻的元素相加,需要用到三个for循环,很短的时间内,我们一致同意用这个算法来解决,以便可以在当堂完成程序。
大神就是大神,永哥编程从来不打草稿,只需灵机一动,一串代码就能浮现在他脑子里。我们的讨论在短短五分钟内就结束了,只上除了潦草的几个方块和曲线再也没有其他。永哥最擅长的语言是java,用起来收放自如得心应手,而在java上,我只算是个初学者,只能在一旁试图跟上他的节奏。
他首先建了一个SumObject类:其中定义了和sum和数组下标indexs。
1 package cn.stdu.edu.cn.domin; 2 3 public class SumObject { 4 private int sum; 5 private String indexs; 6 7 public SumObject(int sum,String indexs) 8 { 9 this.sum=sum; 10 this.indexs=indexs; 11 } 12 public int getSum() { 13 return sum; 14 } 15 public void setSum(int sum) { 16 this.sum = sum; 17 } 18 public String getIndexs() { 19 return indexs; 20 } 21 public void setIndexs(String indexs) { 22 this.indexs = indexs; 23 } 24 }
首先是一个求和的函数,具体的思路就是将每一个数都与剩下相邻的数进行求和,这样就用到了三个for循环语句。
1 public void sum(int number[]) 2 { 3 int sum=0; 4 String indexs=""; 5 for(int i=0;i<number.length;i++) 6 { 7 for(int j=0;j<number.length;j++) 8 { 9 for(int n=i;n<=j;n++) 10 { 11 sum=sum+number[n]; 12 indexs=indexs+n; 13 } 14 SumObject sumObject=new SumObject(sum,indexs); 15 sum=0; 16 indexs=""; 17 sumList.add(sumObject); 18 } 19 } 20 }
二十分钟到了,我们的位置互换,接下来编写的任务就落在了我的头上,下面的任务就是对所求的和进行比较找出最大的进行输出,另外在java语句上的问题永哥对我也有一些指点,虽然在语言上并不熟悉,但还是顺利的完成了。
1 public SumObject MaxOfSum() 2 { 3 int max=0; 4 String indexs=""; 5 for(int i=0;i<sumList.size();i++) 6 { 7 if(max<sumList.get(i).getSum()) 8 { 9 max=sumList.get(i).getSum(); 10 indexs=sumList.get(i).getIndexs(); 11 } 12 } 13 SumObject sumObject=new SumObject(max,indexs); 14 return sumObject; 15 }
最后加入main函数检验我们的结果:
1 public static void main(String[] args) { 2 int num[]={-1,-2,3,60,-7,9,11,-32}; 3 SearchMaxSum searchMaxSum=new SearchMaxSum(); 4 searchMaxSum.sum(num); 5 SumObject sumObject=searchMaxSum.MaxOfSum(); 6 System.out.println(sumObject.getSum()+" "+sumObject.getIndexs()); 7 }
运行结果:
76 23456
前面一项是最大和,后面一项是数组下标。
========================================
以上是我们课堂上的过程,在课下我们对这道题的时间复杂度的问题上对算法再次进行了讨论。
如果对第一个元素其到第n个元素的值是负数或零,则不必考虑其与后面的元素相加,直接将前面的和归零,从下一个元素开始再和后面的元素进行相加。反之继续相加得出最大值。这样我们就得到了一个复杂度为线性的算法。不必浪费时间去对每一种情况进行分析。机智的永哥以光速按照我们后来的思想写下了如下代码,简单粗暴,直接有效。
1 /** 2 * @Name: bestSearchMaxSum 3 * @Description: 在求子数组的最大和 4 * @Author: 张永&吴盈盈 5 * @Version: V1.00 6 * @Create Date: 2014-3-10 7 * @Parameters:整型数组number[],SumObject对象maxSumObject 8 * @Return: SumObject对象 9 */ 10 11 public SumObject bestSearchMaxSum(int number[], SumObject maxSumObject) 12 { 13 int i; 14 int max=0; 15 String indexStart=""; 16 String indexEnd=""; 17 for ( i = 0; i < number.length; i++ ) 18 { 19 if ( (max= number[i] + max) > 0 ) 20 { 21 indexEnd = i+""; 22 } 23 else 24 { 25 indexStart = ""+(i + 1); 26 max = 0; 27 } 28 if ( max > maxSumObject.getSum() ) 29 { 30 maxSumObject.setSum(max); 31 } 32 } 33 maxSumObject.setIndexs(indexStart+indexEnd); 34 return maxSumObject; 35 }
=================================
我们本次课堂作业也算圆满的完成了。在这次作业的过程中突然想起前几日看老师给推荐的《人月神话》这本书上提到的一个经验法则:
对软件任务的进度安排:
1/3计划 1/6编码 1/4构建测试和早期系统测试 1/4系统测试,所有的构建已完成
后两者对于我们来说还尚早。但前两者已经很能说明问题。
在课上我们用五分钟来对算法进行计划,然后用四十分钟写出了一个时间复杂度较高的程序,显然这并不符合这个经验法则,对于我们来讲也是走了弯路浪费了更多的时间。急于求成往往会是丢了西瓜捡了芝麻。可能编码对于我来说还有很大的问题,但是这终究不是设计一个程序中最主要的问题所在,以后应当培养更加灵活的思维,给之后的工作做好万全的准备。