问题:连续子序列最大和,其实就是求一个序列中连续的子序列中元素和最大的那个。 最大子序列和是指,给定一组序列,如 [1,-3,2,4,5],求子序列之和的最大值,对于该序列来讲,最大子序列之和为 2 + 4 + 5 = 11。java
解决方案有不少:一、两个for循环 2 动态规划 3分治算法 4 在线算法算法
一、 2个for循环的算法,思想是:以每个数为第一个数,在这些序列中找出最大值 举例的步骤以下:dom
二、动态规划 思想是:以每一个数为该子串的最后一个数,找出这个子串的最大值,而后在这些最大值中找最大值this
对上面的数进行举例.net
d[0]=1code
d[1]=max(d[0]-3,-3)=-2blog
d[2]=max(d[1]+2,2)=2递归
d[3]=max(d[2]+4,4)=6get
d[4]=max(d[3]+5,5)=11it
3 分治的作法 思想是:用二分查找的思路,先找出左子列的最大连续值,接着找出右子列的最大连续值,接着找出中间 的跨左右子列的最大值,而后取左子列、右子列、中间列值中的最大值。 举例说明:
以 4 - 3 -2 说明
lm 表明左字列最大值,rm表明右子列最大值,bm表明跨边界最大值
在线算法:思想是:循环里的j变量理解成序列的起点,可是这个起点有时会跳过若干数,当当前计算的序列a[i]到a[j]的和ThisSum一旦为负时,则ThisSum归 零,由于for循环的循环变量加1,则从a[j+1]开始加和,即起点跳到了负数和的子序列的下一个数字。
注:我将全部的全部的算法都作了部分修改,能适合所有为负数的状况 具体代码:
import java.util.Random;
public class MaxSub {
static int Upper=100000;
static int a[]=new int [Upper];
public static void main(String[] args) { for(int i=0;i<Upper;i++){ Random random = new Random(); a[i]=random.nextInt(100)-110; } long start = System.currentTimeMillis(); //两个for循环的算法 algorithm(a); long stop =System.currentTimeMillis(); System.out.println((stop-start)+"ms"); long start1 = System.currentTimeMillis(); //动态规划算法 alogrithm1(a); long stop1 =System.currentTimeMillis(); System.out.println((stop1-start1)+"ms"); long start2 = System.currentTimeMillis(); //在线算法 algorithm2(a); long stop2 =System.currentTimeMillis(); System.out.println((stop2-start2)+"ms"); long start3 = System.currentTimeMillis(); //分治算法 alogrithm3(a); long stop3 =System.currentTimeMillis(); System.out.println((stop3-start3)+"ms"); } public static void algorithm(int a[]){ long Max=a[0]; long temp; for(int i=0;i<a.length-1;i++){ temp=a[i]; // 这个地方很重要 if(temp>Max) Max=temp; for(int j=i+1;j<a.length;j++) { temp = temp+a[j]; if(temp>Max) Max=temp; } } if(a[a.length-1]>Max) Max=a[a.length-1]; System.out.println(Max); } public static void alogrithm1(int a[]){ long d[] = new long [a.length]; d[0] = a[0]; for(int i=1;i<a.length;i++){ d[i]=Max(d[i-1]+a[i],a[i]); } long max=d[0]; for(int i=1;i<d.length;i++){ if(max<d[i]) max=d[i]; } System.out.println(max); } //找两个数中的最大值 public static long Max(long a,long b){ return a>b?a:b; } public static void algorithm2(int a[]) { int temp=0; int maxSum, thisSum; maxSum = thisSum = 0; for (int i = 0; i < a.length; i++) { if(a[i]<0) temp++; thisSum += a[i]; if (maxSum < thisSum) maxSum = thisSum; //当累加和为负数时,则从新赋值thisSum else if (thisSum < 0) thisSum = 0; } if(a.length==temp){ int tempMax=a[0]; for(int i=1;i<a.length;i++){ if(tempMax<a[i]) tempMax=a[i]; } maxSum=tempMax; } System.out.println(maxSum); } public static void alogrithm3(int a[]){ int max = submax(a,0,a.length-1); System.out.println(max); } public static int submax(int a[],int left,int right){ //递归退出的条件 if(left==right) return a[left]; //取中心点 int center =(left+right)/2; //取左边的连续和最大值 int leftMax = submax(a,left,center); //取右边的连续和最大值 int rightMax =submax(a,center+1,right); //为了去中间部分的最大值,先取左边跨边界的最大连续和 int leftBorderMax=a[center],leftMaxTemp=a[center]; for(int i=center-1;i>=left;i--){ leftMaxTemp=leftMaxTemp+a[i]; if(leftBorderMax<leftMaxTemp) leftBorderMax=leftMaxTemp; } //为了去中间部分的最大值,再取右边跨边界的最大连续和 int rightBorderMax=a[center+1],rightMaxTemp=a[center+1]; for(int i=center+2;i<=right;i++){ rightMaxTemp=rightMaxTemp+a[i]; if(rightBorderMax<rightMaxTemp) rightBorderMax=rightMaxTemp; } return Math.max(Math.max(leftMax,rightMax),leftBorderMax+rightBorderMax); }
}
各个算法的相互比较:
从上表能够看出:在线算法时间最少,在线算法和动态算法差距不大,分治算法稍微比动态算法差一点
四种算法的时间复杂度为:o(n2),,o(n),o(nlogn),o(n),上图的实验结果基本符合算法复杂度的分析
参考文献:【1】http://www.javashuo.com/article/p-qlqjzfio-ge.html
【2】https://blog.csdn.net/jiaohanhan/article/details/71809357