最大子段和問題描述
給定由 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); }
每天進步一點點,日記月累就會變成大牛!