最大子段和問題描述
給定由 n 個整數(可能為負整數)組成的序列a1,a2,a3...an,求該數列中連續子段和最大!
例如:當(a1,a2,a3,a4,a5)=(-2,11,-4,13,-5,-2)時,最大字段和為 20 (11 + (-4) + 13);
以下例子都是以int data[6] = {-2,11,-4,13,-5,-2}; int n = 6;
初始化數組:
//初始化數組
private static Integer[] array = {-2, 11, -4, 13, -5, -2};
算法一:對所有滿足0<=i<=j<=n的(i,j)整數對進行迭代,對每個整數對,程序都要計算array[i...j]的總和,並檢驗該總和是否大於迄今為止的最大總和
這段代碼簡潔明了,便於理解,但是程序執行的速度很慢,時間復雜度為O(n^3)
/**
* 時間復雜度為 O(n^3)
*/
public static Integer maxSum1() {
int maxSum = 0; //存儲最大子段和
int tempSum; //臨時存儲最大子段和
for (int i = 0; i < array.length - 1; i++) {
for (int j = i; j < array.length; j++) {
tempSum = 0;
for (int k = i; k <= j; k++) {
tempSum += array[k];
if (tempSum > maxSum) {
maxSum = tempSum;
}
}
}
}
return maxSum;
}
算法二:對於算法一有一個明顯的缺點,大量的計算重復。大家可以注意到:
這段代碼簡潔明了,便於理解,相比算法一程序執行的速度較快,時間復雜度為O(n^2)
注意:array[i...j]的總和與前面計算出的總和(array[i...j-1])密切相關,只需要在其基礎上累加即可,無需大量重復計算!
/**
* 時間復雜度為 O(n^2)
*/
public static Integer maxSum2() {
int maxSum = 0; //存儲最大子段和
int tempSum; //臨時存儲最大子段和
for (int i = 0; i < array.length - 1; i++) {
tempSum = 0;
for (int j = i; j < array.length; j++) {
tempSum += array[j];
if (tempSum > maxSum) {
maxSum = tempSum;
}
}
}
return maxSum;
}
算法三:可以采用分治算法求解,采用二分法進行二分,然后進行遞歸求解,分別求出左邊連續子段和最大值,右邊連續子段和最大值,以及左邊和右邊連續子段和最大值之和,三者進行比較,從中選擇一個最大值進行返回!(這個值即就是當前划分的小區間中最大值)
注意:這段代碼不太便於理解,但是程序相對於算法二執行的速度快,時間復雜度為O(n*logn)
/**
* 采用分治算法
* 時間復雜度為 O(n*logN)
*/
public static Integer maxSum3(int left, int right) {
int maxSum = 0;
if (left == right) { //遞歸結束條件
if (array[left] > 0) {
maxSum = array[left];
} else {
maxSum = 0;
}
return maxSum;
}
int mid = (left + right) / 2;
int leftMaxSum = maxSum3(left, mid); //遞歸求解左部分最大值
int rightMaxSum = maxSum3(mid + 1, right); //遞歸求解右部分最大值
//求解左邊最大值和右邊最大值之和
int leftMax = 0; //記錄左邊最大值
int leftMaxTemp = 0; //記錄左邊最大值的臨時變量
for (int i = mid; i >= left; i--) {
leftMaxTemp += array[i];
if (leftMaxTemp > leftMax) {
leftMax = leftMaxTemp; //左邊最大值放在 leftMax
}
}
int rightMax = 0;
int rightMaxTemp = 0;
for (int j = mid + 1; j <= right; j++) {
rightMaxTemp += array[j];
if (rightMaxTemp > rightMax) {
rightMax = rightMaxTemp; //右邊最大值放在 rightMax
}
}
maxSum = leftMax + rightMax;//(左邊最大值和右邊最大值之和)計算第 3 種情況的最大子段和
//比較(左邊最大值)和(右邊最大值)以及(兩邊最大值之和)進行比較,從中選擇一個最大值返回
if (maxSum < leftMaxSum) {
maxSum = leftMaxSum;
}
if (maxSum < rightMaxSum) {
maxSum = rightMaxSum;
}
return maxSum;
}
算法四:使用動態規划來求解 ,由data[]數組我們易知,當maxSumTemp > 0時,maxSumTemp = data[i] + maxSumTemp (越加越大),否則maxSumTemp = data[i](不然越加越小)
這段代碼便於理解,但是程序相對於算法三執行的速度最快,時間復雜度為O(n)
/**
* 時間復雜度為 O(n)
*/
public static Integer maxSum4() {
int maxSum = array[0];
int maxSumTemp = array[0]; //初始化
for (int i = 1; i < array.length; i++) {
if (maxSumTemp > 0) { //最大值臨時變量只有大於零,才可能越加越大
maxSumTemp += array[i];
} else { //最大值臨時變量只有小於零,直接等於data[i],否則越加越小
maxSumTemp = array[i];
}
if (maxSumTemp > maxSum) { //判斷賦值
maxSum = maxSumTemp;
}
}
return maxSum;
}
測試代碼:
public static void main(String[] args) {
// Integer maxSum = maxSum1();
// Integer maxSum = maxSum2();
// Integer maxSum = maxSum3(0, array.length - 1);
Integer maxSum = maxSum4();
System.out.println("maxSum = " + maxSum);
}
每天進步一點點,日記月累就會變成大牛!
