算法設計--在數組中找求和最大的連續子串
問題:輸入具有n個整數的向量arr,輸出向量的任意連續子向量和的最大值
特殊情況(1、當向量都為正數時,為整個向量
2、當向量都為負數時,為0,即空子串
)
1、O(n2)的算法 (循環對所有情況進行遍歷)
1 #include <stdio.h>
2 #define max(a,b) ((a>b)?a:b)
3 #define max3(a,b,c) ((a>b)?((a>c)?a:c):((b>c)?b:c))
4
5 int find1(int arr[], int n){
6 int i,j,sum,maxsofar;
7 maxsofar = 0;
8
9 for(i=0; i<n; i++){
10 sum = 0;
11 for(j=i; j<n; j++){
12 sum += arr[j];
13 maxsofar = max(sum, maxsofar);
14 }
15 }
16 return maxsofar;
17 }
其中有個小細節就是 註意sum(i, j-1) 和 sum(i, j)的關系,不要每次在求和的時候從頭(i的位置)開始,那樣會使復雜度變為O(n3)
2、O(nlogn)算法
基於分治原理的算法:首先將n的原問題劃分為大小基本相等的兩個子問題,我們分別稱為a和b子問題,可以遞歸找出a和b問題的最大子向量,稱為maxa 和 maxb。
但他們兩個之間的最大值不一定使我們求得n問題的最優解,還有一種可能是跨越a和b的邊界,我們稱之為c,c情況的最優解為maxc。
那麽問題變成了如何求解maxc?
我們可以發現,maxc中在a的部分為a中包括a的右邊界的最大值,maxc中在b的部分為b中包括b的左邊界的最大值,因此可以在O(N)的時間內算出maxc
因此得到T(N) = 2T(N/2) + O(N)
推導得到T(N) = O(nlogn)
1 int find2(int arr[], int s_p, int e_p){
2 int m, sum, i, maxsofar, lmaxsofar, rmaxsofar;
3 maxsofar = 0;
4
5 if(s_p == e_p){
6 return maxsofar;
7 }
8 else if(s_p == e_p){
9 return max(arr[s_p],0);
10 }
11 else{
12 m = (s_p + e_p) / 2;
13
14 lmaxsofar = 0;
15 sum = 0;
16 for(i=m; i>=s_p; i--){
17 sum += arr[i];
18 lmaxsofar = max(sum, lmaxsofar);
19 }
20
21 rmaxsofar = 0;
22 sum = 0;
23 for(i=m+1; i<=e_p; i++){
24 sum += arr[i];
25 rmaxsofar = max(sum, rmaxsofar);
26 }
27
28 return max3(lmaxsofar+rmaxsofar,find2(arr, s_p, m), find2(arr, m+1, e_p));
29 }
30 }
3、O(n)算法
先上代碼,代碼非常簡短,理解起來比較困難,但是執行效率非常高
1 int find3(int arr[], int n){ 2 int i,maxsofar,maxendinghere; 3 maxsofar = 0; 4 maxendinghere = 0; 5 6 for(i=0; i<n; i++){ 7 maxendinghere = max(maxendinghere + arr[i], 0); 8 maxsofar = max(maxsofar, maxendinghere); 9 } 10 11 return maxsofar; 12 }
假設我們已經解決了x[0,n-1]的問題,利用分治算法的原理:前i個元素中,最大總和子數組要麽在前i-1個元素中,要麽其結束位置在i處。
分析其結束為止在i處的情況,那麽子向量中除去i處的元素組成的子向量一定是x[0,i-1]中結束位置為i-1的最大子向量。
看代碼中的關鍵變量為maxendinghere:在循環語句的第一個賦值語句之前,maxendinghere是結束位置為i-1的最大子向量的和;賦值語句將其修改為結束位置為i的最大子向量的和。若加上x[i]後結果依然為正值,則結束位置在i的最大子向量值就為maxendinghere+x[i],如果為負值,則重置為0。
算法設計--在數組中找求和最大的連續子串