這幾天一直在讀Weiss的數據結構書(Data Structures and Algorithm Analysis in C:Second Edition),其中第二章是關於簡單的算法分析(引入大O記號等工具),以「求連續子數組的最大和問題」爲例,進行了一些說明和闡釋。最大子數組和問題(原書翻譯爲「最大的子序列和問題」)實際上我去年夏天暑假在家刷學院OJ的時候就見過,後來秋天開算法課,在上機時也有碰到。在網上看到這仍是一道經典的面試題目,在此結合Weiss的書作一點總結性討論。
html
一個整數數組中的元素有正有負,在該數組中找出一 個連續子數組,要求該連續子數組中各元素的和最大,這個連續子數組便被稱做最大連續子數組。好比數組{2,4,-7,5,2,-1,2,-4,3}的最大連續子數組爲{5,2,-1,2},最大連續子數組的和爲5+2-1+2=8。問題輸入就是一個數組,輸出該數組的「連續子數組的最大和」。
ios
這個問題我所見的有四種不一樣時間複雜度的算法。暴力模擬顯然是最慢的,而應用動態規劃思想,能夠獲得一個O(N)級別的線性時間算法,再它的基礎上稍微變形,能夠獲得一個更簡潔的形式。Talk is cheap, let's show codes.面試
int MaxSubsequenceSum1(const int A[],int N) { int ThisSum=0 ,MaxSum=0,i,j,k; for(i=0;i<N;i++) for(j=i;j<N;j++) { ThisSum=0; for(k=i;k<=j;k++) ThisSum+=A[k]; if(ThisSum>MaxSum) MaxSum=ThisSum; } return MaxSum; }
int MaxSubsequenceSum2(const int A[],int N) { int ThisSum=0,MaxSum=0,i,j,k; for(i=0;i<N;i++) { ThisSum=0; for(j=i;j<N;j++) { ThisSum+=A[j]; if(ThisSum>MaxSum) MaxSum=ThisSum; } } return MaxSum; }
由於最大子序列和可能在三處出現,整個出如今數組左半部,或者整個出如今右半部,又或者跨越中間,佔據左右兩半部分。遞歸將左右子數組再分別分紅兩個數組,直到子數組中只含有一個元素,退出每層遞歸前,返回上面三種狀況中的最大值。算法
根據這樣的分析,寫出代碼:數組
static int MaxSubSum(const int A[],int Left,int Right) { int MaxLeftSum,MaxRightSum; //左、右部分最大連續子序列值 int MaxLeftBorderSum,MaxRightBorderSum; //從中間分別到左右兩側的最大連續子序列值 int LeftBorderSum,RightBorderSum; int Center,i; if(Left == Right)Base Case if(A[Left]>0) return A[Left]; else return 0; Center=(Left+Right)/2; MaxLeftSum=MaxSubSum(A,Left,Center); //遞歸調用 MaxRightSum=MaxSubSum(A,Center+1,Right); MaxLeftBorderSum=0; LeftBorderSum=0; for(i=Center;i>=Left;i--) { LeftBorderSum+=A[i]; if(LeftBorderSum>MaxLeftBorderSum) MaxLeftBorderSum=LeftBorderSum; } MaxRightBorderSum=0; RightBorderSum=0; for(i=Center+1;i<=Right;i++) { RightBorderSum+=A[i]; if(RightBorderSum>MaxRightBorderSum) MaxRightBorderSum=RightBorderSum; } //比較各類狀況,求出最大值 int max1=MaxLeftSum>MaxRightSum?MaxLeftSum:MaxRightSum; int max2=MaxLeftBorderSum+MaxRightBorderSum; return max1>max2?max1:max2; }
另一份寫的更清晰的代碼:數據結構
/* 求三個數中的最大值 */ int Max3(int a,int b,int c) { int Max = a; if(b > Max) Max = b; if(c > Max) Max = c; return Max; }
int MaxSubSum2(int *arr,int left,int right) { int MaxLeftSum,MaxRightSum; //左右邊的最大和 int MaxLeftBorderSum,MaxRightBorderSum; //含中間邊界的左右部分最大和 int LeftBorderSum,RightBorderSum; //含中間邊界的左右部分當前和 int i,center; //遞歸到最後的基本狀況 if(left == right) if(arr[left]>0) return arr[left]; else return 0; //求含中間邊界的左右部分的最大值 center = (left + right)/2; MaxLeftBorderSum = 0; LeftBorderSum = 0; for(i=center;i>=left;i--) { LeftBorderSum += arr[i]; if(LeftBorderSum > MaxLeftBorderSum) MaxLeftBorderSum = LeftBorderSum; } MaxRightBorderSum = 0; RightBorderSum = 0; for(i=center+1;i<=right;i++) { RightBorderSum += arr[i]; if(RightBorderSum > MaxRightBorderSum) MaxRightBorderSum = RightBorderSum; } //遞歸求左右部分最大值 MaxLeftSum = MaxSubSum2(arr,left,center); MaxRightSum = MaxSubSum2(arr,center+1,right); //返回三者中的最大值 return Max3(MaxLeftSum,MaxRightSum,MaxLeftBorderSum+MaxRightBorderSum); } /* 將分支策略實現的算法封裝起來 */ int MaxSubSum2_1(int *arr,int len) { return MaxSubSum2(arr,0,len-1); }
以上代碼時間複雜度爲O(NlogN)工具
不可貴出,針對這個問題,遞推公式是DP[i] = max{DP[i-1] + A[i],A[i]};既然轉移方程出來了,意味着寫一層循環就能夠解決這個問題。spa
將這個轉移方程變爲形象的if-else判斷,代碼(來源於Weiss的書)爲:.net
int MaxSubSum(int arr[],int len) { int i; int MaxSum = 0; int ThisSum= 0; for(i=0;i<len;i++) { ThisSum+= arr[i]; if(ThisSum > MaxSum) MaxSum = ThisSum; /*若是累加和出現小於0的狀況, 則和最大的子序列確定不可能包含前面的元素, 這時將累加和置0,從下個元素從新開始累加 */ else if(ThisSum< 0) ThisSum= 0; } return MaxSum; }
最後貼一份我去年暑假過學校OJ題時AC的代碼。翻譯
#include <stdio.h> #include <stdlib.h> #include <iostream> using namespace std; int MaxsumUlt(int * arr, int size) { int maxSum = 0xf0000000; int sum = 0; for(int i = 0; i < size; ++i) { if(sum < 0) { sum = arr[i]; } else { sum += arr[i]; } if(sum > maxSum) { maxSum = sum; } } return maxSum; } int main(){ int n; while(cin>>n){ int a[n]; for(int i = 0;i<n;i++) cin>>a[i]; printf("%d \n",MaxsumUlt(a,n)); } }
Weiss的這本數據結構書的確不錯,跟我大一下學校開數據結構課程所用的教材(兩本清華出版的)簡直雲泥之別。只不過對於沒有算法基礎的初學者而言,可能會有必定難度,篇幅簡略必然帶來理解難度的增長。
http://blog.csdn.net/ns_code/article/details/20942045
http://blog.sina.com.cn/s/blog_60d6fadc0101369g.html
——————————————————————————————————————
轉載請註明出處,AllZY的博客園: http://www.cnblogs.com/allzy/