• 软件工程(2018)第三次个人作业


    软件工程(2018)第三次个人作业


    前方高能:本次作业中含有大量基础知识,请不要嘲笑我QAQ

    第三次作业来了。选择看似相比有难度的(1)(其实是看不懂(2)在干什么)

    题目要求:题目(1):最大连续子数组和(最大子段和)

    背景
    问题: 给定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。
    

    读完题目,第一想到就是数组解决问题。定义数组a[]输入变量后,遍历数组,将数组每个元素分别开始向后逐步取和,将这些和存到数组b[]中,最后比较数组b[]中,取最大值即可。

    看起来用到的方法都是数组的基本方法,第三次作业果然还是简单(心里长舒一口气)

    所以我的代码构思是在某堂课上随手完成的,大体思路算法有了后,在自己计算机上实现就行

    但是实现又遇到了各种问题,一些在C上容易实现的方法,在Eclipse上还没有试过

    问题(1):Java中数组的定义(没错第一个问题就是这个)

    解决:参考《疯狂JAVA讲义》。JAVA中数组可动态定义或静态定义,前者定义后分配指定个数个内存,后者定义后直接给数组赋值,由系统来确定分配多少个空间。

    		int[] intArr;
    		intArr=new int[] {5,6,8,20};//静态定义
    		int[] intArr2= {5,6,8,21};//简化静态定义方式
    
    		int[] intArr3;
    		intArr3=new int[4];//动态定义
    		int[] intArr4=new int[4];//简化动态定义方式
    

    与此同时,我想着把JAVA中所有关于数组的基本方法学习一下,数组的输入输出当然是最重要的

    输入:利用循环逐个输入元素,循环变量控制条件是数组的length变量,此时须注意动态定义中会使length有可能大于我们程序运行时要输入的元素个数,在此我有疑问,JAVA是否可以实现动态数组,我们先完成作业,然后再考虑这个

    而JAVA中输入方法为下

    		Scanner x = new Scanner(System.in);
    		int n1=x.nextInt();
    

    输出:利用循环遍历数组元素,逐个输出,输出方法如下

    		System.out.println();//ln是换行
    

    同时,了解到JAVA中有foreach循环遍历数组,看起来能简化代码

    		for(int m:intArr)
    			System.out.println(m);//foreach用法,注意m相当于一个临时变量,所以foreach方法无法完成数组的输入。
    

    由问题(1)引发的关于数组的基本用法先学到这里,基本够用

    这样程序代码可以写了,以下是我的程序代码

    import java.util.Scanner;
    
    public class Sz
    {
    	int n;//数组元素个数
    	static int[] a=new int[10] ;//输入数组
    	static int[] b=new int[10];//以每个元素为首元素的组合集
    	public static void createArray(int n)//数组输入
    	{
    		for(int i=0;i<n;i++)
    		{
    			Scanner x = new Scanner(System.in);
    			int n1=x.nextInt();
    			a[i]=n1;
    		}
    	}
    	public static int getMax(int i,int n)//生成数组第i个元素为首元素的组合最大的组合
    	{
    		int[] c=new int[10];//用于循环中求该元素为首元素的组合中最大的组合
    		c[i]=a[i];
    		for(int j=i;j<n-1;j++)
    		{
    			c[j+1]=c[j]+a[j+1];
    		}
    		for(int m:c)
    		{
    			if(b[i]<m)
    				b[i]=m;
    		}
    		return b[i];
    	}
    	public static int getbMax(int[] b)//将b[]中最大元素筛选出来
    	{
    		int max=0;
    		for(int m:b)
    		{
    			if(max<m)
    				max=m;
    		}
    		return max;
    	}
    
    	public static void main(String[] args)
    	{
    		Scanner x = new Scanner(System.in);
    		int n=x.nextInt();
    		createArray(n);
    		for(int i=0;i<n;i++)
    		{
    			getMax(i,n);
    		}
    		System.out.println(getbMax(b));
    	}
    }
    

    附上程序运行截图

    样例1:数组(a[1],a[2],a[3],a[4],a[5],a[6])=(-2,11,-4,13,-5,-2)

    样例2:(a[1],a[2],a[3],a[4],a[5])=(-2,6,4,-7,-2)

    注:以上代码是我最终版本代码,没以下问题,我是先写完程序再写博客,所以要保证代码都OK才会回忆我当时编程的心路过程(我觉得边写代码边写博客真的破坏思路,当然我的梦想是能边写边编而不影响思路,正如老师说过的直播写代码,我觉得OK)(我也有主播梦想)

    写代码后调试修改了一些小错误,问题不大的,没有写出来。接下来碰到一个我不太理解的错误,虽然Eclipse强大的帮助修改能力教我将所以变量、方法加上static属性,但是我还是要弄懂有啥区别

    问题(2)JAVA中static的用法

    老规矩,上网学

    这次应该是问题明确又基础,很容易找到回答

    static表示“全局”或者“静态”的意思,用来修饰成员变量和成员方法,也可以形成静态static代码块,但是Java语言中没有全局变量的概念。
    被static修饰的成员变量和成员方法独立于该类的任何对象。也就是说,它不依赖类特定的实例,被类的所有实例共享。
    
    只要这个类被加载,Java虚拟机就能根据类名在运行时数据区的方法区内定找到他们。因此,static对象可以在它的任何对象创建之前访问,无需引用任何对象。
    
    用public修饰的static成员变量和成员方法本质是全局变量和全局方法,当声明它类的对象市,不生成static变量的副本,而是类的所有实例共享同一个static变量。
    
    static变量前可以有private修饰,表示这个变量可以在类的静态代码块中,或者类的其他静态成员方法中使用(当然也可以在非静态成员方法中使用--废话),但是不能在其他类中通过类名来直接引用,这一点很重要。实际上你需要搞明白,private是访问权限限定,static表示不要实例化就可以使用,这样就容易理解多了。static前面加上其它访问权限关键字的效果也以此类推。
    
    static修饰的成员变量和成员方法习惯上称为静态变量和静态方法,可以直接通过类名来访问,访问语法为:
    类名.静态方法名(参数列表...)
    类名.静态变量名
    
    用static修饰的代码块表示静态代码块,当Java虚拟机(JVM)加载类时,就会执行该代码块(用处非常大,呵呵)。
    
    1、static变量
    按照是否静态的对类成员变量进行分类可分两种:一种是被static修饰的变量,叫静态变量或类变量;另一种是没有被static修饰的变量,叫实例变量。
    
    两者的区别是:
    对于静态变量在内存中只有一个拷贝(节省内存),JVM只为静态分配一次内存,在加载类的过程中完成静态变量的内存分配,可用类名直接访问(方便),当然也可以通过对象来访问(但是这是不推荐的)。
    对于实例变量,没创建一个实例,就会为实例变量分配一次内存,实例变量可以在内存中有多个拷贝,互不影响(灵活)。
    
    所以一般在需要实现以下两个功能时使用静态变量:
      在对象之间共享值时
      方便访问变量时
    
    
    2、静态方法
    静态方法可以直接通过类名调用,任何的实例也都可以调用,
    因此静态方法中不能用this和super关键字,不能直接访问所属类的实例变量和实例方法(就是不带static的成员变量和成员成员方法),只能访问所属类的静态成员变量和成员方法。
    因为实例成员与特定的对象关联!这个需要去理解,想明白其中的道理,不是记忆!!!
    因为static方法独立于任何实例,因此static方法必须被实现,而不能是抽象的abstract。
    
    例如为了方便方法的调用,Java API中的Math类中所有的方法都是静态的,而一般类内部的static方法也是方便其它类对该方法的调用。
    
    静态方法是类内部的一类特殊方法,只有在需要时才将对应的方法声明成静态的,一个类内部的方法一般都是非静态的
    

    以上内容摘自这篇博客(特意看了下欢迎转载,具体示例也有)

    个人总结:static修饰的变量和方法称为静态变量,可以在类中直接引用变量、直接调用方法

    问题解决后,经过测试也都ok,突然想到,我用Eclipse调试不像c一样熟练,而且我还没掌握Eclipse调试的方法

    问题(3)Eclipse中如何调试

    网上学习。Eclipse支持设置断点进行调试(否则程序直接运行到底)。右键代码行头数字设置breakpoint(或直接双击数字)(breakpoint更有详细设置,支持条件断点调试,有丶厉害啊这个)F5逐步调试,F6具体进入方法,F7跳出方法,CTRL+F2结束调试,嗯,我记住了

    调试过程中,可将鼠标直接放在程序变量上显示变量,更可以设置出Variables窗口查看其余变量(Window——show view——Other——debug——Variables),这样跟C语言调试几乎完美相同了,或许有更多功能待挖掘

    接下来是测试

    覆盖方法

    (1))语句覆盖:选择合适用例,所有语句被执行一次。

    语句覆盖是指选择足够的测试用例,使得运行这些测试用例时,被测程序的每一个语句至少执行一次,其覆盖标准无法发现判定中逻辑运算的错误。
    

    (2)判定覆盖:每个判定至少取一次真、一次假。

    判定覆盖是设计足够多的测试用例,使得程序中的每一个判断至少获得一次“真”和一次“假”,即使得程序流程图中的每一个真假分支至少被执行一次。
    

    (3)条件覆盖:每个条件的各种可能结果至少满足一次。

    条件覆盖是指选择足够的测试用例,使得运行这些测试用例时,判定中每个条件的所有可能结果至少出现一次,但未必能覆盖全部分支。
    

    (4)判定条件覆盖:同时满足判断覆盖和条件覆盖。

    判定条件覆盖是设计足够的测试用例,得使判断中每个条件的所有可能取值至少执行一次,同时每个判断本身所有可能结果也至少执行一次。缺点是忽略了条件的组合情况。
    

    (5)条件组合覆盖:所有组合情况都要覆盖一次。

    在白盒测试法中,选择足够的测试用例,使得每个判定中条件的各种可能组合都至少出现一次。显然,满足“条件组合覆盖”的测试用例是一定满足“判定覆盖”、“条件覆盖”和“判定/条件覆盖”的。
    

    选定的覆盖方法(判定/条件覆盖)我采用的是判定条件覆盖:

    (1)max>b[] max>0
    
    (2)max>b[] max=0
    
    (3)max<b[] max>0
    
    (4)max<b[] max=0
    

    选择用例:

    (1)测试用例:{-2,11,-4,9}
    (2)测试用例:{-2,-3,-4,-5}
    (3)测试用例:{1,2,3,4}
    (4)测试用例:{-5,-4,-3,-1}
    

    建立JUNIT测试单元,测试代码如下

    import static org.junit.Assert.*;
    
    import org.junit.Test;
    
    public class SzTest
    {
    	int[] a=new int[] {-2,11,-4,9};
        static int[] b=new int[] {14,16,5,9};
    	@Test
    	public void testGetMax()
    	{
    		assertEquals(14,new Sz().getMax(0,4));
    		assertEquals(16,new Sz().getMax(1,4));
    		assertEquals(5,new Sz().getMax(2,4));
    		assertEquals(9,new Sz().getMax(3,4));
    	}
    	@Test
    	public void testGetbMax()
    	{
    		assertEquals(16,new Sz().getbMax(b));
    	}
    }
    

    进行测试,出现问题(4)测试失败

    如图

    java.lang.AssertionError: expected:<14> but was:<0>

    测试结果是0?不应该啊。于是设置断点逐步调试,发现问题。由于Sz中a[]是静态变量,在那个类中可以被程序直接引用修改。而在Sztest类的调用中,Sz的a[]的值一直为0。所以程序输出为0。为了测试用,我将Sz.getMax中临时定义a[]的值为{-2,11,-4,9}(仅测试用,不影响原程序),并且将Sztest中定义的a[]删除(这的a[]没有意义了),重新调试,OK。如图。
    


    接下来将剩余测试测完,直接列图了

    注:这里的0是因为都是负数,所以默认全用0取代


    注:这里同理

    单元测试完成!

    后记:本次作业完成,问题也都圆满解决,最后调试出现的问题是靠自己调试分析完成有点成就感。下次见~!

    对了,动态数组还没实现!

    我觉得,如果能像建立链表一样建立一个元素,然后每次输入元素都连接上一个元素(链表靠地址连接,数组就靠建立数组时分配的连续内存这个顺序连接),输入结束后,数组也建立完成。通过思考和逐渐实践,我觉得可行,但是总感觉自己理解不深导致没有弄出来。最后在网上发现了跟我思路特别像的大佬实现了(侵删)

        // 定义一个初始长度为0的数组,用来缓存数据
        private String[] src = new String[0];
        // 增加
        public void add(String s)
        {
            //定义新数组,长度是原数组长度+1
            String[] dest = new String[src.length+1];
            //将原数组的数据拷贝到新数组
            System.arraycopy(src, 0, dest, 0, src.length);
            //将新元素放到dest数组的末尾
            dest[src.length]=s;
            //将src指向dest
            src=dest;
        }
    

    豁然开朗,突然想起来C语言中例如a[]中的"a"实际上是一个地址,可以指向别的地址,这样我就可以将两个数组的"数组头"连接起来,模拟建立链表时光标的移动。不过我有疑问了:既然是指向,那原数组是不是应该及时删除从而减少内存呢?删除后才可以算做动态数组的完美建立。感觉又学到了丶东西。

    本次作业也收获颇丰呢(最终代码)

  • 相关阅读:
    [FCC] Cash Register 计算找零
    [Linux] 快速修改hosts访问github
    关于Unicode和乱码恢复的基本原理
    oracle修改字符集,12c修改varchar2长度
    nginx 配置前端多个根路径
    Nginx 反向代理,流量转发到固定内网 IP 方法
    【.NET Core】关于.NET Core的AOT
    对于layui导出所有数据 , 和数据中有15位以上纯数字 或者是数字 + 字母E 的情况下会便成为科学计数问题的解决 [06.28 底部补充]
    Unity 2D 卷轴 视差 滚动 延迟 卡顿 解决办法
    Redisson分布式
  • 原文地址:https://www.cnblogs.com/L1aw/p/8686341.html
Copyright © 2020-2023  润新知