一、题目描述
题目:最大连续子数组和(最大子段和)
问题: 给定n个整数(可能为负数)组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的子段和的最大值。当所给的整数均为负数时定义子段和为0,依此定义,所求的最优值为: Max{0,a[i]+a[i+1]+…+a[j]},1<=i<=j<=n。例如,当(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)时,最大子段和为20。
二、问题分析
1、算法描述
解决该题的的初步思路是从数组中的第一个元素开始,遍历所有的元素,求和。假若这个数组有n个元素,那么连续的组成子数组的元素个数可以是1,2,...,n,此时分别从连续数组元素个数为1,2...n的数组求和,再比较对应个数下数组的和得出最大的一个和即为所求结果。这个思路虽然简单易懂,但算法时间、空间复杂度较高,不是一个最优算法,但该算法可以简化。我们可以先定义一个变量sum用来存储最大连续子数组的和,并赋初值为0;另外定义一个中间变量num,赋初值为0,从数组b[n]的第一个元素b[i],i=0,1,2,3...n开始遍历所有元素,使num=num+b[i],再将sum=num,若每次加完某一元素求和后的结果大于sum在之前保存的结果,那么将num的值又赋给sum,否则不做任何操作。当所有的元素遍历结束,在sum中保存的结果即为最大连续子数组之和,最后判断sum的值是否大于0,如果大于0,则输出最大连续子数组之和sum,否则输出0。这样的话大大简化了算法的时间复杂度。
2、关键代码
public int max() {
for(int i=0;i<n;i++){
int num=0;
for(int j=i;j<n;j++){
num=num+a[j];
if(num>sum){
sum=num;
}
}
}
if(sum>=0) {
System.out.println(sum);
return sum;
}
else
System.out.println(0);
return 0;
}
3、流程图
4、运行题目中的测试用例
三、单元测试
1、覆盖方法
(1)语句覆盖:选择合适用例,所有语句被执行一次。
语句覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每一个语句至少执行一次,其覆盖标准无法发现判定中逻辑运算的错误。
(2)判定覆盖:每个判定至少取一次真、一次假。
判定覆盖是设计足够多的测试用例,使得程序中的每一个判断至少获得一次“真”和一次“假”,即使得程序流程图中的每一个真假分支至少被执行一次。
(3)条件覆盖:每个条件的各种可能结果至少满足一次。
条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,判定中每个条件的所有可能结果至少出现一次,但未必能覆盖全部分支。
(4)判定条件覆盖:同时满足判断覆盖和条件覆盖。
判定条件覆盖是设计足够的测试用例,得使判断中每个条件的所有可能取值至少执行一次,同时每个判断本身所有可能结果也至少执行一次。缺点是忽略了条件的组合情况。
(5)条件组合覆盖:所有组合情况都要覆盖一次。
在白盒测试法中,选择足够的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。显然,满足“条件组合覆盖”的测试用例是一定满足“判定覆盖”、“条件覆盖”和“判定/条件覆盖”的。
2、所选定的覆盖方法(判定/条件覆盖)
在该单元测试中,我采用的是判定条件覆盖:
(1)sum>num sum>=0
(2)sum>num sum<0
(3)sum>num sum>=0
(4)sum<num sum<0
3.选择用例
(1)测试用例一:{-2,11,-4,9}
(2)测试用例二:{1,2,3,4}
(3)测试用例三:{-1,-1,-1,-1}
(4)测试用例四:{-2,-3,1,-3}
四、测试结果展示
1、程序自动运行测试截图
(1)测试用例一:
(2)测试用例二:
(3)测试用例三:
(4)测试用例四:
2、单元测试
(1)单元测试代码:
package test03;
import static org.junit.jupiter.api.Assertions.*;
class TestDemo01Test {
@BeforeEach
void setUp() throws Exception {
}
@Test
void test() {
int []a = new int[] {1,2,3,4};
assertEquals(10, new TestDemo01(a).max());
int []b = new int[] {-1,-1,-1,-1};
assertEquals(0, new TestDemo01(b).max());
int []c = new int[] {-2,-3,1,-3};
assertEquals(1, new TestDemo01(c).max());
int []d = new int[] {-2,11,-4,9};
assertEquals(16, new TestDemo01(d).max());
}
}
(2)单元测试截图:
3、运行结果分析
(1)Runs:总共有1个测试方法,已经运行了1个;
(2)Errors:抛出异常的测试方法的个数是0;
(3)Failures:表示失败的测试方法的个数是0;
(4)打钩:表示通过测试方法,测试结果完全正确。
五、代码链接
传送门https://git.coding.net/mocnld/ruangongthree.git
六、总结与心得体会
这次作业是第一次作业和第二次作业的综合,难度要比前两次要大,但恰恰就是这样才能锻炼我们的能力。这次作业中,在参考老师给出的解决最大连续子数组的博文后,我学习了其中提出的解决最大连续子数组之和的五种方法中的一种方法,用Java编写出解决这个问题的代码;另外编写这个代码不难,难的是如何去找一个合适的测试用例去测试它。在同学的帮助和我自身出的努力下,我较好的完成了此次作业,虽然还是有很多的缺点,但相比以前进步了许多,相信能通过软件工程这门课,能够提升我的编程、项目开发的流程及与人合作能力,为以后的工作打一个良好的基础,相信我能做到。