問題定義: 假設在100, 113, 110, 85, 105, 102, 86, 63, 81, 101, 94, 106, 101, 79, 94, 90 , 97 這樣一組數據中,它的一個實際背景是,每個值反應的是每天的股票價格,我們需要求出哪天買進股票,哪天賣出股票,得到的收益最大。
解法:
1.使用暴力求解
我們需要遍歷每一種可能的買進和賣出日期組合,也就是窮盡每一種可能的子數組。也就是n中任意去2個座標的組合數。顯然它是O(n^2)量級的。
2.使用遞歸分治的思想
2.1轉化思想
把問題 轉化爲最大子數組問題 先對數組進行轉化, 轉化爲收益數組
13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7 第i個值,表示第i天的價格 - 第i-1天的價格
2.2 分類討論
最大子數組出現的可能情況來源於3個, 我們不妨把數組進行等分 min = (low + high ) / 2;
1.情況一, 最大子數組來源於 左半數組
2. 情況二,最大子數組來源於 右半數組
3. 情況三, 最大數組穿過數組中間。
2.3 遞歸迭代
本模式滿足遞歸迭代模式, 運用遞歸的思想 可以得到 maxSubArry(A) = Max( maxSubArray(A-left), maxSubArray(A-right), maxCrosSubArrray(A));
基於這樣的求解思想,給出對應的程序實現
//該類用於最大子數組
class SubArray{
int sum;
int low;
int high;
public SubArray(int sum, int low, int high){
this.sum = sum;
this.low = low;
this.high = high;
}
}
//最大子數組的搜索
public static SubArray findMaxCroSubArray(int[] array, int low, int mid, int high){
//算法要求是尋找誇區間的最大子數組
//思想 遍歷法, 化歸爲求最大的連續和, 對稱性, 左右對稱
//先求左半邊
int leftSum = Integer.MIN_VALUE;
int sum = 0;
int leftIndex = mid;
for(int i = mid; i>=low; i--){
sum += array[i];
if(sum > leftSum){
leftSum = sum;
leftIndex = i;
}
}
//得到了左半邊的最大連續和 以及 對應的下標
int rightSum = Integer.MIN_VALUE;
int rightIndex = mid+1;
sum = 0;
for(int i = mid+1; i<=high; i++){
sum += array[i];
if(sum > rightSum){
rightSum = sum;
rightIndex = i;
}
}
SubArray resultSubArray = new SubArray(leftSum + rightSum, leftIndex, rightIndex);
return resultSubArray;
}
//求連續數組
public static SubArray findMaxSubArray(int[] array, int low, int high){
//思想, 分類整合的思想 遞歸的思想
//分情況, 1.遞歸的求左半邊的最大連續子數組 2.遞歸的求右半邊的最大連續子數組 3.求最大連續跨區間數組 然後取三者值
//遞歸的終止條件是 如果low==high 那麼 返回array[low] 即可
if(low == high){
SubArray resultSubArray = new SubArray(array[low], low, high);
return resultSubArray;
}
int mid = (low + high) / 2;
SubArray leftSubArray = findMaxSubArray(array, low, mid);
SubArray rightSubArray = findMaxSubArray(array, mid+1, high);
SubArray croSubArray = findMaxCroSubArray(array, low, mid, high);
System.out.print("array:");
for(int i =low; i<=high; i++){
System.out.print(array[i]+" ");
}
//13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7
System.out.println();
if(leftSubArray.sum >= rightSubArray.sum && leftSubArray.sum >= croSubArray.sum){
//此時最大是leftSubArray
System.out.println(leftSubArray.sum+", "+leftSubArray.low+", "+leftSubArray.high);
return leftSubArray;
}else if(rightSubArray.sum >= leftSubArray.sum && rightSubArray.sum >= croSubArray.sum){
System.out.println(rightSubArray.sum+", "+rightSubArray.low+", "+rightSubArray.high);
return rightSubArray;
}else {
System.out.println(croSubArray.sum+", "+croSubArray.low+", "+croSubArray.high);
return croSubArray;
}
}
下面給出遞歸的構造結果過程:
array:13 -3
13, 0, 0
array:-25 20
20, 3, 3
array:13 -3 -25 20
20, 3, 3
array:-3 -16
-3, 4, 4
array:-23 18
18, 7, 7
array:-3 -16 -23 18
18, 7, 7
array:13 -3 -25 20 -3 -16 -23 18
20, 3, 3
array:20 -7
20, 8, 8
array:12 -5
12, 10, 10
array:20 -7 12 -5
25, 8, 10
array:-22 15
15, 13, 13
array:-4 7
7, 15, 15
array:-22 15 -4 7
18, 13, 15
array:20 -7 12 -5 -22 15 -4 7
25, 8, 10
array:13 -3 -25 20 -3 -16 -23 18 20 -7 12 -5 -22 15 -4 7
43, 7, 10
43
18 20 -7 12