最大股票收益問題(數組最大差問題) 問題描述
給定一個數組,儲存著按照時間排序的股票價格,第 i 個位置的元素為第 i 次交易時的股票價格;現假設只允許你進行一次買,然後在某一時刻賣出(單只股票),請設計演算法,求解你可能獲得的最大收益,如果股價是非增的,則收益為0。 O(n2) 解法
將數組每個元素與其後的所有元素比較,選擇增差最大的一對兒。
時間複雜度為:
T(n)=(n−1)+(n−2)+......+1
O(nlogn) 解法 演算法思想
採用分而治之的思想,將數組對半分,則最大收益分以下3中情況:
- 在前半段中買並賣
- 在後半段中買並賣
- 在前半段買,在後半段賣
對於第1種和第2種情況,只需要將其看作一個規模減小的新的問題,也就是迭代我們的演算法;對於第3種情況,需要在前半段找最小元素 min ,在後半段找最大元素 max ,則 max−min 是第3種情況下的可能的最優解。 虛擬碼
void stock(int K[], int startIndex, int endIndex){ int length = endIndex - startIndex; if(1==length) return 0; if(2==length) return K[endIndex]-K[startIndex] > 0 ? K[endIndex]-K[startIndex] : 0; if(length>2){ midIndex = (startIndex+endIndex)/2; min = findMin(K, startIndex, midIndex ); max = findMax(K, midIndex+1, endIndex); mayGet = max(stock(K,startIndex,midIndex), stock(K,startIndex,midIndex), max-min); return mayGet > 0 ? mayGet : 0; }} 複雜度
T(n)=2∗T(n2)+n2+n2
解得:
T(n)=O(nlogn)
O(n) 解法 演算法思想
預先處理數組,轉化為求數組最大子段和問題,然後使用最大子段和的求解演算法解決該問題。 步驟: 預先處理數組
將數組的每一項減去其緊挨著的前一項,首項設為0;
其含義為:將股價數組,變為股價增幅數組。
如:
K = [3,5,1,2,5,8,9,6]
處理之後變為
K = [0,2,-4,1,3,3,1,-3]
預先處理之後,原問題就轉化為求數組最大子段和的問題。
例如,在原數組中很顯然最大收益為 9−1=8 ,則在預先處理之後的數組中,只需要從 1 之後的第一個元素開始到 9 所對應的元素為止,進行累加即可,為 1+3+3+1=8 。
這其中的道理也很簡單,因為預先處理之後的數組儲存的正是增量,將兩個元素之間的所有增量累加,得到的正是兩個元素的差值。
預先處理演算法虛擬碼:
void preProcess(int K[], int n){ int i; i = n-1; while(i>0){ K[i] = k[i]-k[i-1]; } K[0] = 0;} 求最大子段和
那麼最大收益的問題就轉化成為了求數組最大子段和的問題,針對最大子段和採用以下演算法。
先看虛擬碼:
void maxSegSum(int K[], int n){ int i; int maxSum=-MAXINT; int tempSum=0; for(i=0; i<n; i++){ tempSum += K[i]; if(tempSum>maxSum) maxSum = tempSum; if(tempSum<0) tempSum=0; } print("max sub-segment sum is %f", maxSum);}
演算法描述:
- 從頭到尾遍曆數組,維持兩個全域變數,maxSum記錄最優解,tempSum記錄從某個位置開始的子段和;
- 每次更新tempSum之後,先和maxSum進行比較,大於maxSum則更新maxSum;然後檢查tempSum是否小於0,如果否繼續累加,如果是則將tempSum歸零;
- 這樣遍曆一遍數組之後,得到的maxSum就是該數組的最大子段和。 演算法綜述 預先處理數組,轉化為最大子段和問題 O(n) ; 使用以上演算法求解最大子段和問題 O(n) 。 時間複雜度
T(n)=O(n)+O(n)
即:
T(n)=O(n)