給定整數A1,A2,...An(可能為負數),求子序列和最大值(如果所有整數為負數,則子序列和為0)。
窮舉法
int MaxSubsequenceSum2(const int A[], int N) {
int MaxSum = 0;
for (int i = 0; i < N; i++ ) { /*1*/
int ThisSum = 0;
/*
內層循環 每次遍歷元素 然后計算從前面數值的和,
然后比較,獲得以該元素為起點的最大值。
*/
for (int j = i; j < N; j++ ) { /*2*/
ThisSum += A[j];
if (ThisSum > MaxSum) {
MaxSum = ThisSum;
}
}
}
return MaxSum;
}
畫了一個簡單的實例圖,輔助理解,主要是做兩次循環遍歷,內層遍歷是 以當前元素為起點,查找到最大子序列和,外層循環,尋找每次以每個元素為起點的最大值。這個方法時間復雜度O(n2)。
1
分治法
分治法的主要思想是把問題分成兩個大致相等的字問題,然后遞歸地對它們求解,這是“分”問題?!爸巍彪A段將兩個子問題的解合并到一起,再做些少量的附加工作,最后得到整個問題的解。
還是先上代碼。代碼有點長,需要慢慢看。
int MaxSubSum(const int A[], int Left, int Right) {
int MaxLeftSum, MaxRightSum;
int MaxLeftBorderSum, MaxRightBorderSum;
int LeftBorderSum, RightBorderSum;
// 基礎情況
if (Left == Right) {
if (A[Left] > 0) {
return A[Left];
} else {
return 0;
}
}
// 遞歸計算
int Center = (Left + Right) / 2;
MaxLeftSum = MaxSubSum(A, Left, Center);
MaxRightSum = MaxSubSum(A, Center+1, Right);
// 計算出中間部分向右 最大連續序列和
MaxLeftBorderSum = 0;
LeftBorderSum = 0;
for (int i = Center; i >= Left; i--) {
LeftBorderSum += A[i];
if (LeftBorderSum > MaxLeftBorderSum) {
MaxLeftBorderSum = LeftBorderSum;
}
}
// 計算出中間部分向右 最大連續序列和
MaxRightBorderSum = 0;
RightBorderSum = 0;
for (int i = Center+1; i <= Right; i++) {
RightBorderSum += A[i];
if (RightBorderSum > MaxRightBorderSum) {
MaxRightBorderSum = RightBorderSum;
}
}
// 獲得三個部分中最大值
int max = MaxLeftSum > MaxRightSum ? MaxLeftSum : MaxRightSum;
int borderMax = MaxLeftBorderSum + MaxRightBorderSum;
max = max > borderMax ? max : borderMax;
return max;
}
int MaxSubsequenceSum3(const int A[], int N) {
return MaxSubSum(A, 0, N-1);
}
這個問題中,最大子序列和可能出現在三個位置。前半部分,后半部分,或者跨越前后部分。如果是跨越前后部分,那么肯定是從中間向左右兩邊的最大值的和。
算法大概有二個部分,一個基準條件,如果只剩下一個元素,則就是當前元素了,這也是遞歸必備的條件。第二部分是遞歸計算左邊最大值,右邊最大值,以及從中間向左和從中間向右的最大值(即跨越前后部分)和。
2
聯機算法
聯機算法,它只對數據進行一次掃描,一旦被讀入并處理,它就不需要被記憶。在任何時刻,算法都可以給出已經讀入的數據給出正確答案。
先展示代碼
int MaxSubsequenceSum4(const int A[], int N) {
int ThisSum, MaxSum;
ThisSum = MaxSum = 0;
for (int i = 0; i < N; i++) {
ThisSum += A[i];
if (ThisSum > MaxSum) {
MaxSum = ThisSum;
} else if (ThisSum < 0) { /*1*/
ThisSum = 0;
}
}
return MaxSum;
}
核心思想:要求最大子序列和,那么前面部分序列的和如果小于0,那么包含該序列和肯定不是最大的(包含該序列,那么和肯定會減少)。
最后,最近開始認真的學習一遍算法部分,也嘗試著把學習的東西記錄下來,也是讓自己更好的理解。算是一個簡單的開篇,下篇寫寫排序部分,算是挖個坑。
參考
- 數據結構與算法分析:C語言描述 2.4.3節
- 算法導論