求解最大連續子數組的內容在《算法導論》這本書上面是做爲分治算法的一個例子來進行講解的,書本上面內容(包括習題)提到了三種解決這一問題的算法,下面是我本身使用C++實現這三種方法的代碼和思路放:算法
1、暴力解法數組
對數組內每個數A[i]進行遍歷,而後遍歷以它們爲起點的子數組,比較各個子數組的大小,找到最大連續子數組函數
#include "stdafx.h" //暴力法求最大子數組和問題 int _tmain(int argc, _TCHAR* argv[]) { int A[8] = { -6, 10, -5, -3, -7, -1, -1 }; int array_length = sizeof(A) / sizeof(A[0]);//數組大小 int sum = -10000;//記錄子數組的和 int low;//記錄子數組的底 int height;//記錄子數組的高 for (int i = 0; i < array_length; i++) { for (int j = i ; j < array_length; j++) { int subarraysum=0;//所遍歷出來的子數組的和 //計算遍歷的子數組之和 for (int k = i; k <= j; k++) { subarraysum += A[k]; } //找出最大的子數組 if (subarraysum>sum) { sum = subarraysum; low = i; height = j; } } } printf("%d %d %d", low, height,sum);//將結果打印出來 getchar(); return 0; }
能夠看到這段程序裏面一共嵌套着三層循環,除了最外面的循環會循環n次外內部的循環都比n次小,此程序的時間複雜度爲O(n3)this
2、分治法spa
這一算法在《算法導論》當中有很詳細的說明,並且上面還有僞代碼,我就不囉嗦了,直接放出個人實現代碼。code
1 #include "stdafx.h" 2 //分治法求最大子數組和問題 3 struct PositioASum { 4 int low; 5 int high; 6 int sum; 7 }; 8 //尋找包含中點位置的最大子數組函數 9 PositioASum MaxCrossingSubarray(int a[], int low, int mid, int high) 10 { 11 //求中點左邊的最大值和最大位置 12 int maxLeft;//記錄左邊的最大位置 13 int maxSumLeft=-10000;//記錄左邊的最大和 14 int sumLeft=0; 15 for (int i = mid; i >= low; i--) 16 { 17 sumLeft += a[i]; 18 if (sumLeft > maxSumLeft) 19 { 20 maxSumLeft = sumLeft; 21 maxLeft = i; 22 } 23 } 24 //求中點右邊的最大值和最大位置 25 int maxRight=mid+1;//記錄右邊的最大位置 26 int maxSumRight = -10000;//記錄右邊的最大和 27 int sumRight = 0;//記錄右邊子數列的和 28 for (int i = mid+1; i <= high; i++) 29 { 30 sumRight += a[i]; 31 if (sumRight > maxSumRight) 32 { 33 maxSumRight = sumRight; 34 maxRight = i; 35 } 36 } 37 PositioASum ps; 38 ps.low = maxLeft; 39 ps.high = maxRight; 40 ps.sum = maxSumLeft + maxSumRight; 41 return ps; 42 } 43 //分治法 44 PositioASum FindMaxSubArray(int a[], int low, int high) 45 { 46 if (low == high) 47 { 48 PositioASum ps; 49 ps.low = low; 50 ps.high = high; 51 ps.sum = a[low]; 52 return ps; 53 } 54 else{ 55 int mid = (low + high) / 2; 56 PositioASum left = FindMaxSubArray(a, low, mid); 57 PositioASum right = FindMaxSubArray(a, mid + 1, high); 58 PositioASum cross = MaxCrossingSubarray(a, low, mid, high); 59 if (left.sum >= cross.sum && left.sum >= right.sum) 60 { 61 return left; 62 } 63 else if (right.sum >= left.sum && right.sum >= cross.sum) 64 { 65 return right; 66 }else{ 67 return cross; 68 } 69 } 70 } 71 72 int _tmain(int argc, _TCHAR* argv[]) 73 { 74 int A[8] = {-1,0,0,0,-1}; 75 PositioASum result = FindMaxSubArray(A, 0, 4); 76 printf("%d %d %d", result.low, result.high, result.sum);//將結果打印出來 77 getchar(); 78 return 0; 79 }
算法的時間複雜度爲O(nlogn),因爲本程序是在數組的原地址上面進行的,因此整體的控件複雜度爲遞歸的時間複雜度+數組所佔的空間爲S(n)+S(logn)=S(n)blog
3、根據書本後面習題所提供的方法遞歸
書本上面提出這樣的一種方法:從數組的左邊界開始,從左到右處理,記錄到目前爲止已經處理過的最大子數組。若已知A[1,2,....,j]的最大子數組,則A[1,2,.....,j,j+1]的最大子數組要麼是A[1,2,....,j]的最大子數組,要麼是某個子數組A[i,....,j+1](1<=i<=j+1)。基於這一思路個人實現過程以下:get
1 #include "stdafx.h" 2 //基於算法導論上面的習題4.1-5來實現的算法 3 int _tmain(int argc, _TCHAR* argv[]) 4 { 5 int A[8] = { -6, 10, -5, 3, 7, -1, -1 }; 6 int array_length = sizeof(A) / sizeof(A[0]);//數組大小 7 int maxSum = A[0];//記錄最大子數組的和 8 int low=0;//記錄最大子數組的底 9 int high=0;//記錄最大子數組的高 10 for (int i = 0; i < array_length-1; i++) 11 { 12 int sum = 0; 13 //尋找以A[i+1]爲終點的最大子數組 14 for (int j = i+1; j>=0; j--) 15 { 16 sum += A[j]; 17 if (sum>maxSum) 18 { 19 maxSum = sum; 20 low = j; 21 high = i + 1; 22 } 23 } 24 } 25 printf("%d %d %d", low, high,maxSum);//將結果打印出來 26 getchar(); 27 return 0; 28 }
4、在線算法it
//在線法求最大子數組和問題 int _tmain(int argc, _TCHAR* argv[]) { int A[8] = { -6, 10, -5, 6, -7, -1, -1 }; int array_length = sizeof(A) / sizeof(A[0]);//數組大小 int sum = 0;//記錄子數組的和 int thisSum = 0; int low=0;//記錄子數組的底 int height=0;//記錄子數組的高 for (int i = 0; i < array_length; i++) { thisSum += A[i]; if (thisSum > sum) { sum = thisSum; } else if (thisSum < 0) { thisSum = 0; } } printf("%d",sum);//將結果打印出來 getchar(); return 0; }
理解這個算法的關鍵是:使用thisSum來計算當前連續子數組的和,若是thisSum小於0,那麼不管後面再加上什麼數字都只會讓子數組變小,因此拋棄當前子數組,從新開始計算子數組的值。
能夠看到這個算法的時間複雜度爲O(n)並且控件複雜度爲S(n),是解決這一個問題很是有效的一個算法。
5、另一種稍好的算法
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 int n;//存儲所輸入的數組長度 4 scanf("%d", &n); 5 int* b = (int*)malloc(n*sizeof(int));//存儲所出入的數組 6 for (int i = 0; i < n; i++) 7 { 8 scanf("%d", &b[i]); 9 } 10 int sum=0;//最大子數列和 11 //尋找最大子數列 12 for (int i = 0; i < n; i++) 13 { 14 int thisSum = 0;//當前數列 15 for (int j = i; j < n; j++) 16 { 17 thisSum += b[j]; 18 if (thisSum>sum) 19 { 20 sum = thisSum; 21 } 22 } 23 } 24 if (sum < 0) 25 { 26 printf("%d",0); 27 }else{ 28 printf("%d", sum); 29 } 30 system("pause"); 31 return 0; 32 }
相比於暴力解法每一次都從i到j地從新計算一次,這種算法每一次只須要在原來計算的基礎上面加上一個數,因此這種算法少了一層循環,時間複雜度爲O(n2)是一種比暴力解法要高效的解法