問題:連續子序列最大和,其實就是求一個序列中連續的子序列中元素和最大的那個。 最大子序列和是指,給定一組序列,如 [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