二维数组中最大的子数组之和
问题:返回二维数组最大子数组之和
要求:1、输入一个二维整形数组,数组里有正数有负数。 2、二维数组中连续的一个子矩阵组成一个子数组。3、求所有子数组最大和
一、解决思路及实现代码:
该算法思路,根据我博客里面一维子数组求和的思路,可以用一个新的二维数组对该二维区域的数组进行求和,例如新的二维数组的第5个位置,就代表从1到5斜对角线的块状区域的和,即1,2,4,5这4个数的和,x个位置表示从1到x的斜对角块状区域的和,利用循环一一求出对应的和,一次循环即可,这个循环复杂度为O(nm)

接着再求出来新的二维数组中,每一个从1到X的块状区域的和均被求出,所以对于任意区域X(如图以具体的例子展示出来),只需要求出其最大值即可。
确定一个点需要两层循环,即循环O(mn),在二维数组中需要求两个点,所以复杂度为O(m2n2)。
求值分3种(或者说4种),第1种情况即从1到X区域有最大值,这种情况在上面求和的过程中已经求出最大,所以再进行考虑。
除了不再考虑的情况,第1种最常见的情况是:
对于任意区域X要求和,只需要获得新的数组X区域右下角的值(第9个数据的值),减去Z区域的右下角的值(第7个数据),减去Y区域右下角的值(第3个数据),加上Z交Y右下角的值即可(第1个数据)。
第2种情况是块状区域包含第一行的时候有最大值,只需要求出获得X区域右下角的值减去Z区域右下角的值最大就行了
第3种情况是块状区域包含第一列的时候有最大值,只需要求出获得X区域右下角的值减去Y区域右下角的值最大就行了
根据上述的条件,实现了以下的算法如下:
package test; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; public class Max { public static void main(String[] args) throws IOException { Integer[][] c=new Integer[100][]; c=ReadFile("D:\\桌面\\软件工程(python)\\arr.txt"); for(int i=0;i<c.length;i++){ for(int j=0;j<c[0].length;j++){ System.out.print(c[i][j]+" "); } System.out.println(); } System.out.println("1:"+GetMaxSUM(c)); } public static Integer[][] ReadFile(String str){ FileReader file = null; try { file = new FileReader(str); } catch (FileNotFoundException e) { e.printStackTrace(); } System.out.println("文件测试数据如下:"); BufferedReader br = new BufferedReader(file);//读取文件 try { String line = br.readLine();//读取一行数据 int lenx = Integer.parseInt(line);//将数据转化为int类型 line = br.readLine();//读取一行数据 int leny = Integer.parseInt(line);//将数据转化为int类型 String []sp = null; String [][]c = new String[lenx][leny]; Integer[][] cc = new Integer[lenx][leny]; int count=0; while((line=br.readLine())!=null) {//按行读取 sp = line.split(" ");//按空格进行分割 for(int i=0;i<sp.length;i++){ c[count][i] = sp[i]; } count++; } for(int i=0;i<lenx;i++){ for(int j=0;j<leny;j++){ cc[i][j] = Integer.parseInt(c[i][j]); } } return cc; } catch (IOException e) { e.printStackTrace(); } return null; } public static int GetMaxSUM(Integer[][] a){ int n=a.length; int m=a[0].length; //分块 Integer[] Max=new Integer[100]; Integer[] Begin=new Integer[100]; Integer[] End=new Integer[100]; Integer[] b=new Integer[100]; for(int i=0;i<n;i++) { //按行分组 for(int j=0;j<m;j++) { b[j]=a[i][j]; } MaxIntArray(b,Max,Begin,End,m,i); } int max=Max[0]; for(int i=0;i<n-1;i++) { if((Begin[i]<=End[i+1]&&Begin[i]>=Begin[i+1])||(End[i]<=End[i+1]&&End[i]>=Begin[i+1])) { max=Max[i+1]+max; } else { //如果不能直接连通,判断代价是否合适 if(Begin[i]>End[i+1]) { int t = Begin[i]-End[i+1]; int s = Begin[i]; int temp=0; for(int k=0;k<t;k++) { temp+=a[i+1][s-k]; } if(temp+Max[i+1]>0) max=temp+Max[i+1]; } if(End[i]<Begin[i+1]) { int t = Begin[i+1]-End[i]; int s = End[i]; int temp=0; for(int k=0;k<t;k++) { temp+=a[i+1][s+k]; } if(temp+Max[i+1]>0) max=temp+Max[i+1]; } } } return max; } public static void MaxIntArray(Integer[] a,Integer[] max,Integer[] begin,Integer[] end,int n,int index){ Integer[] Max=new Integer[100]; Max[0] = 0; int i = 0;//数组下标 int j = 0;//最大值数组下标 int temp=0;//中间变量 //记录子数组的起始位置和末位 // Integer[] Bg=new Integer[100]; Integer[] Bg={-1,-1,-1,-1,-1}; Integer[] Ed=new Integer[100]; while(i<n){ if(temp+a[i]>=Max[j]) { temp=temp+a[i]; Max[j]=temp; if(Bg[j]==-1) Bg[j]=i; Ed[j]=i; i++; } else if(temp+a[i]<Max[j]&&temp+a[i]>0) { temp=temp+a[i]; i++; } else if(temp+a[i]<=0) { i++; j++; Max[j]=0; temp=0; } } max[index] = Max[0]; int q=0; for(int k=0;k<=j;k++){ if(Max[k]>max[index]) { max[index]=Max[k]; q=k; } } begin[index]=Bg[q]; end[index]=Ed[q]; } }
二、单元测试
该测试项目是读取txt文件中的数据,并进行测试,在txt文件中,行和列的后面不能有空格,有空格时会在单元测试阶段出现测试错误,没有通过测试。
2.1、创建测试用例:
| 测试编号 | 测试用例 | 预期输出 | 实际输出 |
| test1 | 3 4 3 6 8 9 2 5 1 8 4 7 3 9 |
65 | 65 |
| test2 |
3 3 6 -8 |
25 | 25 |
| test3 | 2 3 -3 6 -2 2 5 0 |
13 | 13 |
| test4 | 3 3 3 6 -5 -2 5 1 0 1 8 |
24 | 24 |
2.2、创建测试环境及代码:
创建测试类:

单元测试代码:
package test; import static org.junit.Assert.*; import static test.Max.GetMaxSUM; import static test.Max.ReadFile; import org.junit.Test; public class MaxTest { @Test public void test1() { String str="D:\\桌面\\软件工程(python)\\test1.txt"; Integer[][] c=ReadFile(str); System.out.println(GetMaxSUM(c)); } @Test public void test2() { String str="D:\\桌面\\软件工程(python)\\test2.txt"; Integer[][] c=ReadFile(str); System.out.println(GetMaxSUM(c)); } @Test public void test3() { String str="D:\\桌面\\软件工程(python)\\test3.txt"; Integer[][] c=ReadFile(str); System.out.println(GetMaxSUM(c)); } @Test public void test4() { String str="D:\\桌面\\软件工程(python)\\test4.txt"; Integer[][] c=ReadFile(str); System.out.println(GetMaxSUM(c)); } }
测试结果:
在txt文件中,若行和列后面有多余的空格,则会出现下面结果:

在正确情况下,结果如下:

在输出最下方会输出该测试用例中的最大子数组之和:

有以上运行结果可知,四组测试用例都通过测试。
三、代码覆盖率:
3.1、安装eclemma插件:
在eclipse中安装eclemma,在eclipse->help->Eclipse Marketplace->搜索eclemma

进行安装。
3.2、覆盖率检测
选择想要测试的单元测试文件或者文件夹,编写成功后右键项目选择Coverage As -> JUnit Test
对代码进行覆盖率的测试结果为:


绿色:代码被执行过 黄色:代码部分被执行过 红色:代码没有被执行过
统计如下图所示:

可以看到每个方法中执行到的代码和未执行的代码,可以看到代码测试覆盖率。看代码,是取得随机数,if判断可能出现不满足,可能满足的情况, 需要多次执行该代码,得到多次的代码覆盖率,然后多次得到的覆盖率合并,得到这个类总的代码测试覆盖率。
四、总结
1、通过Eclipse插件可以快速验证测试用例的代码覆盖率,在大型项目中,合格的测试用例覆盖率能够很好的减少因临时修改代码而导致的重大BUG,但不建议盲目追求代码覆盖率,尤其是中小型项目。
2、因为测试用例不完全,所以整个项目的代码覆盖率也是不完整的,如果要详细得到整个项目所有的代码覆盖率至少要把所有的代码测试用例都执行一遍,会很麻烦。
3、二维数组求其最大子数组之和还有情况,这考虑的只是里面小矩形的最大值,在二维数组还可以是环形的,连通的,不同的形状得到的结果不同。
4、在进行代码覆盖率时,不能忘记测试异常情况,空指针,或者其他异常后,执行异常代码,也会增加代码测试覆盖率的。
5、若得到代码测试覆盖率较高,则能看出代码安全性会比较能保证。
浙公网安备 33010602011771号