数组中最大的子数组之和
一、程序分析
1,使用Eclipse 进行编译,求数组中最大数组之和。
注意:
- 题目说的是子数组,是连续的
- 题目只要求和,不需要返回数组的具体位置
- 数组的元素是整数,所以可能包含正整数,零,负整数。
import java.util.Scanner; public class Calculate { private static Scanner in;//创建一个输入对象 public static void main(String[] args) { // TODO Auto-generated method stub Calculate i =new Calculate(); int Max = 0; int Max1 = 0; in = new Scanner(System.in); System.out.print("请输入数组的长度:"); int j=in.nextInt();//输入数组的长度 System.out.print("请依次输入数组:"); int Arr[]=new int[j];//输入数组 for(int a = 0;a < j; a++){ Arr[a]=in.nextInt(); } int Arr1[]=new int [j]; for(int a = 0; a < j; a++) { for(int b = 0; b < j; b++){ Arr1[b]=Arr[(b + a)%(j)]; } Max=i.findMaxArr(Arr1); if(Max > Max1) { Max1 = Max; } } System.out.print("最大的子数组和为"+Max1); } public int findMaxArr(int[] arr) { int Arr = 0; int MaxArr = 0; int c = arr.length; int Location2=0; int a; for ( a = 0; a < c; a++) { Arr += arr[a]; if (Arr < 0) { Arr = 0; } if (Arr > MaxArr) { MaxArr = Arr; } } for(a = Location2; a >= 0; a--) { if(MaxArr-arr[a]==0) { break; } } if (MaxArr == 0) { for ( a = 0; a < c; a++) { if (a == 0) { MaxArr = arr[a]; } if (arr[a] > MaxArr) { MaxArr = arr[a]; } } } return MaxArr; } }
2,进行单元测试代码如下:
import static org.junit.Assert.*; import org.junit.Before; import org.junit.Test; public class CalculateTest { private Calculate mcalculate; int[] Calculate = {1,4,5,-5,-1}; @Before public void setUp() throws Exception { mcalculate =new Calculate(); } @Test public void testMain() throws Exception { } @Test public void testFindMaxArr() throws Exception { assertEquals(10, mcalculate.findMaxArr(Calculate), 0); } }
二、代码风格说明
1,分行定义变量
int Arr = 0; int MaxArr = 0; int c = arr.length; int Location2=0; int a;
2,缩进风格(indent)
在有关代码风格的问题中,最为显眼的可以说就是代码的缩进(Indent)了。所谓缩进,是通过在每一行的代码左端空出一部分长度,更加清晰地从外观上体现出程序的层次结构。
三、单元测试验证程序的正确性、以及运行结果截图
1,使用的是Eclipse自带的单元测试工具JUnit,首先点击文件,新建,选择Junit Text Case,然后选择要测试的类和方法,如图1,

图1操作过程图
创建成功如图2,
图2创建成功界面图
2,运行结果如图3:

图3运行结果
3.测试结果
(1)最大子数组为部分,测试用例[1,4,5,-5,-1],最大子数组和10(如图4)

图4 测试结果成功
(2)最大子数组为部分,测试用例[1,3,-1],最大子数组和4(如图5)

图5 测试结果成功
3,数组全为零,测试用例[0,0,0],最大子数组和0(如图6)

图6 测试结果成功
4,数组全为负,测试用例[-1,-3,-1],最大子数组和-1(如图7)

图7 测试结果成功
5,最大子数组为真子集,测试用例[2,3,4],最大子数组和9(如图8)

图8 测试结果成功
6,最大子数组为单一数,测试用例[1,-2,5,-1,-1],最大子数组和9(如图9)

图9 测试结果成功
四、性能分析结果及改进
1,尽量减少对变量的重复计算
例如:明确一个概念,对方法的调用,即使方法中只有一句语句,也是有消耗的,包括创建栈帧、调用方法时保护现场、调用方法完毕时恢复现场等。所以例如下面的操作:
int c = arr.length; for ( a = 0; a < c; a++) { Arr += arr[a]; if (Arr < 0) { Arr = 0; } if (Arr > MaxArr) { MaxArr = Arr; } }
而不是使用:
for ( a = 0; a <arr.length; a++) { Arr += arr[a]; if (Arr < 0) { Arr = 0; } if (Arr > MaxArr) { MaxArr = Arr; } }
2,尽量避免随意使用静态变量
3,不要创建一些不使用的对象,不要导入一些不使用的类
这毫无意义,如果代码中出现"The value of the local variable i is not used"、"The import java.util is never used",那么请删除这些无用的内容。
4,请知道,在java中if (i == 1)和if (1 == i)是没有区别的,但从阅读习惯上讲,建议使用前者
平时有人问,"if (i == 1)"和"if (1== i)"有没有区别,这就要从C/C++讲起。
在C/C++中,"if (i == 1)"判断条件成立,是以0与非0为基准的,0表示false,非0表示true,尽管Java的"if (i == 1)"和"if (1 == i)"在语义上没有任何区别,从阅读习惯上讲,建议使用前者会更好些。
例如(加粗代码):
for(a = Location2; a >= 0; a--) { if(MaxArr-arr[a]==0) { break; } } if (MaxArr == 0) { for ( a = 0; a < c; a++) { if (a == 0) { MaxArr = arr[a]; } if (arr[a] > MaxArr) { MaxArr = arr[a]; } } }
补充:
效能分析
1.JProfiler功能说明
使用Jprofiler工具对上面所写的Java代码进行效能分析。
1、使用可视化工具分析得到的图如下所示:

2、Live momery (内存视图)

JProfiler的内存视图部分可以提供动态的内存使用状况更新视图和显示关于内存分配状况信息的视图。所有的视图都有几个聚集层并且能够显示现有存在的对象和作为垃圾回收的对象。
①All Objects (所有对象)
显示类或在状况统计和尺码信息堆上所有对象的包。你可以标记当前值并显示差异值。
②Record Objects (记录对象 )
显示类或所有已记录对象的包。你可以标记出当前值并且显示差异值。
③Allocation Call Tree (分配访问树)
显示一棵请求树或者方法、类、包或对已选择类有带注释的分配信息的J2EE组件。
④Allocation Hot Spots (分配热点)
显示一个列表,包括方法、类、包或分配已选类的J2EE组件。你可以标注当前值并且显示差异值。对于每个热点都可以显示它的跟踪记录树。
⑤Class Tracker (类追踪器)
类跟踪视图可以包含任意数量的图表,显示选定的类和包的实例与时间。
3、Heap walker (堆遍历)

在JProfiler的堆遍历器(Heap Walker)中,你可以对堆的状况进行快照并且可以通过选择步骤下寻找感兴趣的对象。堆遍历器有五个视图.
①Classes (类 ):
显示所有类和它们的实例,可以右击具体的类"Used Selected Instance"实现进一步跟踪。
②Allocations(分配)
为所有记录对象显示分配树和分配热点。
③References(索引)
为单个对象和“显示到垃圾回收根目录的路径”提供索引图的显示功能。还能提供合并输入视图和输出视图的功能。
④Time(时间)
显示一个对已记录对象的解决时间的柱状图。
⑤Inspections(检查 )
显示了一个数量的操作,将分析当前对象集在某种条件下的子集,实质是一个筛选的过程。
⑥-Graph(图表)
你需要在references视图和biggest视图手动添加对象到图表,它可以显示对象的传入和传出引用,能方便的找到垃圾收集器根源。
4、CPU views(cpu视图)
(1)
JProfiler 提供不同的方法来记录访问树以优化性能和细节。线程或者线程组以及线程状况可以被所有的视图选择。所有的视图都可以聚集到方法、类、包或J2EE组件等不同层上。
①Call Tree(访问树)
显示一个积累的自顶向下的树,树中包含所有在JVM中已记录的访问队列。JDBC,JMS和JNDI服务请求都被注释在请求树中。请求树可以根据Servlet和JSP对URL的不同需要进行拆分。
②Hot Spots(热点)
显示消耗时间最多的方法的列表。对每个热点都能够显示回溯树。该热点可以按照方法请求,JDBC,JMS和JNDI服务请求以及按照URL请求来进行计算。
③Call Graph(访问图)
显示一个从已选方法、类、包或J2EE组件开始的访问队列的图。
④Method Statistis(方法统计)
显示一段时间内记录的方法的调用时间细节
(2)在快照左侧选择CPU视图,点击到调用树,可以在右侧看到每个函数占用的CPU时间,图片如下:

(3)在快照左侧选择CPU视图,点击到热点图,图片如下:

(4)异常值检测

5、Threads(线程视图)

JProfiler通过对线程历史的监控判断其运行状态,并监控是否有线程阻塞产生,还能将一个线程所管理的方法以树状形式呈现。对线程剖析。
①Thread History(线程历史)
显示一个与线程活动和线程状态在一起的活动时间表。
②Thread Monitor(线程监控)
显示一个列表,包括所有的活动线程以及它们目前的活动状况。
③Thread Dumps(线程转储)
显示所有线程的堆栈跟踪。
6、Monitor &locks

①Current Locking Graph (当前锁定图表)
显示JVM中的当前锁定情况。
②Current Monitors (当前监视器)
显示当前正在等待或阻塞中的线程操作。
③Locking History Graph(锁定历史图表)
显示记录在JVM中的锁定历史。
④Monitor History(监控器历史)
显示等待或者阻塞的历史。
⑤Monitor Usage Statistics(监控器使用统计)
计算统计监控器监控的数据。
2.CPU分析
通常一个方法的执行时间越长则占用CPU的资源则越多,在JProfiler里就是通过方法的执行时间来描述对CPU的使用情况。
通过CPU views视图的Method Statistics子视图可以看到一段时间里涉及类方法的执行时间,这些时间是从开始记录到查看该视图这段时间所发生的执行时间的合计,如此可以准确反映出真实场景下的方法执行情况。
一般是用LoadRunner压一段时间后再查看该视图,通过占用时间的不同,找出系统里最耗时的类方法进行调优解决问题。
发现执行一次请求,响应时间不能满足需求时,通过这种CPU时间占有的方式分析可优化点是一种简单而有效的方式。
3.线程分析
线程的运行情况可以直接反应出系统的瓶颈所在,对线程一般有以下三个关注点:
- Web容器的线程最大数管理,Web容器允许开启的线程数与系统要求的最大并发数有一定的对应关系,通常是Web容器运行的线程数略大于最大并发数,以Tomcat为例,在{$tomcat}/conf/server.xml文件的Connector选项里配置maxThreads,它的默认值时150;
- 线程阻塞;
- 线程死锁。
通过Threads视图可以观察到项目的线程情况,如下图所示:


浙公网安备 33010602011771号