方法1:暴力計算法算法
i 表示子列開始索引數組
j 表示子列結束索引ide
k(i<k<j) 輔助計算 i~j之間子列和優化
public int method1(int[] arr) { int maxSum = 0; for (int i = 0; i < arr.length; i++) { for (int j = i; j < arr.length; j++) { int tempMax = 0; for (int k = i; k <= j; k++) { tempMax += arr[k]; } if (tempMax > maxSum) { maxSum = tempMax; } } } return maxSum; }
三層for循環,時間複雜度 T(N) = n^3spa
方法2:暴力破解優化3d
由於方法一每次都是從 i 加到 j ,而 j 每次隻日後掃描變化一個,因此直接將 k 循環省略在 j 循環中計算code
public int method2(int[] arr) { int maxSum = 0; for (int i = 0; i < arr.length; i++) { int tempSum = 0; for (int j = i; j <= arr.length; j++) { tempSum += arr[j];
if (tempSum > maxSum) {
maxSum = tempSum;
}
}
}
return maxSum;
}
兩層for循環,T(N) = n^2blog
方法3:分治策略,將問題劃分爲更小的問題,解決後再返回來求最優解索引
/** * 爲了與方法1和2有一樣的調用接口 * @param arr * @return */ public int method3(int[] arr) { return divideAndConquer(arr, 0, arr.length - 1); } /** * 將數組劃分爲兩部分,先計算左半部分最大子列和,再計算又半部分最大子列和,再從分界線向左向右分別掃描獲取最大子列和取得跨界最大子列和 * * @param arr * @param left * @param right * @return */ private int divideAndConquer(int[] arr, int left, int right) { /** * 若是索引相等,表示已經劃分該部分爲最小問題,元素個數爲1 * 若該元素大於0,則對計算下一步運算有幫助,返回原值 * 若該元素小於0,則不管向左向右加都會減少相鄰子列的和,因此捨棄該元素,返回0 */ if (left == right) { if (arr[left] > 0) { return arr[left]; } else { return 0; } } /* 下面是"分"的過程 */ int mid = (left + right) / 2; /* 求最大左子列和 */ int maxLeftSum = divideAndConquer(arr, left, mid); /* 求最大右子列和 */ int maxRightSum = divideAndConquer(arr, mid + 1, right); /* 求跨界最大子列和 */ int maxLeftBorderSum = 0; int maxRightBorderSum = 0; int leftBorderSum = 0; int rightBorderSum = 0; /* 從中線向左掃描 */ for (int i = mid; i >= left; i--) { leftBorderSum += arr[i]; if (leftBorderSum > maxLeftBorderSum) { maxLeftBorderSum = leftBorderSum; } } /* 從中線向右掃描 */ for (int i = mid + 1; i < right; i++) { rightBorderSum += arr[i]; if (rightBorderSum > maxRightBorderSum) { maxRightBorderSum = rightBorderSum; } } /* 返回該分段中最左,最右,跨界三者中最大數做爲該分段最大子列和 */ return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); } /** * 求三整數最大值 * 計算順序 * a > b ? (a > c ? a : c) : (b > c ? b : c) * @param a * @param b * @param c * @return */ private int max3(int a, int b, int c) { return a > b ? a > c ? a : c : b > c ? b : c; }
T(N) = T(N/2) + cN接口
求解得時間複雜度 T(N) = nlogn
方法4:在線處理方法,掃描一個元素解決當前階段最大子列和
/** * 在線處理 * * @param arr */ public int method4(int[] arr) { int maxSeqSum = 0; int tempMax = 0; for (int i = 0; i < arr.length; i++) { tempMax += arr[i]; // 向右累加 if (tempMax > maxSeqSum) { maxSeqSum = tempMax; // 發現更大的和則更新當前結果 } else if (tempMax < 0) { // 若是當前子列和爲負 tempMax = 0; // 則不可能使後面的部分和增大,拋棄之 } } return maxSeqSum; }
每一個元素最少都要掃描一遍,時間複雜度T(N) = n
運行代碼
public class SearchMaxQueueSum { public static void main(String[] args) { int[] arr = new int[]{-1, 3, -2, 4, -6, 1, 6, -1}; SearchMaxQueueSum searchMaxQueueSum = new SearchMaxQueueSum(); int seqSumFromMethod1 = searchMaxQueueSum.method1(arr); int seqSumFromMethod2 = searchMaxQueueSum.method3(arr); int seqSumFromMethod3 = searchMaxQueueSum.method3(arr); int seqSumFromMethod4 = searchMaxQueueSum.method4(arr); System.out.println("method1: "+seqSumFromMethod1); System.out.println("method2: "+seqSumFromMethod2); System.out.println("method3: "+seqSumFromMethod3); System.out.println("method4: "+seqSumFromMethod4); } public int method1(int[] arr) { int maxSum = 0; for (int i = 0; i < arr.length; i++) { for (int j = i; j < arr.length; j++) { int tempMax = 0; for (int k = i; k <= j; k++) { tempMax += arr[k]; } if (tempMax > maxSum) { maxSum = tempMax; } } } return maxSum; } public int method2(int[] arr) { int maxSum = 0; for (int i = 0; i < arr.length; i++) { int tempSum = 0; for (int j = i; j <= arr.length; j++) { tempSum += arr[j]; if (tempSum > maxSum) { maxSum = tempSum; } } } return maxSum; } /** * 爲了與方法1和2有一樣的調用接口 * @param arr * @return */ public int method3(int[] arr) { return divideAndConquer(arr, 0, arr.length - 1); } /** * 將數組劃分爲兩部分,先計算左半部分最大子列和,再計算又半部分最大子列和,再從分界線向左向右分別掃描獲取最大子列和取得跨界最大子列和 * * @param arr * @param left * @param right * @return */ private int divideAndConquer(int[] arr, int left, int right) { /** * 若是索引相等,表示已經劃分該部分爲最小問題,元素個數爲1 * 若該元素大於0,則對計算下一步運算有幫助,返回原值 * 若該元素小於0,則不管向左向右加都會減少相鄰子列的和,因此捨棄該元素,返回0 */ if (left == right) { if (arr[left] > 0) { return arr[left]; } else { return 0; } } /* 下面是"分"的過程 */ int mid = (left + right) / 2; /* 求最大左子列和 */ int maxLeftSum = divideAndConquer(arr, left, mid); /* 求最大右子列和 */ int maxRightSum = divideAndConquer(arr, mid + 1, right); /* 求跨界最大子列和 */ int maxLeftBorderSum = 0; int maxRightBorderSum = 0; int leftBorderSum = 0; int rightBorderSum = 0; /* 從中線向左掃描 */ for (int i = mid; i >= left; i--) { leftBorderSum += arr[i]; if (leftBorderSum > maxLeftBorderSum) { maxLeftBorderSum = leftBorderSum; } } /* 從中線向右掃描 */ for (int i = mid + 1; i < right; i++) { rightBorderSum += arr[i]; if (rightBorderSum > maxRightBorderSum) { maxRightBorderSum = rightBorderSum; } } /* 返回該分段中最左,最右,跨界三者中最大數做爲該分段最大子列和 */ return max3(maxLeftSum, maxRightSum, maxLeftBorderSum + maxRightBorderSum); } /** * 求三整數最大值 * 計算順序 * a > b ? (a > c ? a : c) : (b > c ? b : c) * @param a * @param b * @param c * @return */ private int max3(int a, int b, int c) { return a > b ? a > c ? a : c : b > c ? b : c; } /** * 在線處理 * * @param arr */ public int method4(int[] arr) { int maxSeqSum = 0; int tempMax = 0; for (int i = 0; i < arr.length; i++) { tempMax += arr[i]; // 向右累加 if (tempMax > maxSeqSum) { maxSeqSum = tempMax; // 發現更大的和則更新當前結果 } else if (tempMax < 0) { // 若是當前子列和爲負 tempMax = 0; // 則不可能使後面的部分和增大,拋棄之 } } return maxSeqSum; } }
運行結果