最大子数组和问题的解
寻找数组A的和最大的非空连续子数组。例如:A[] = {1, -2, 3, 10, -4, 7, 2, -5}的最大子数组为3, 10, -4, 7, 2,其最大和为18。数组元素都为负时,返回最大负值。
解法一:暴力枚举,O(N^2)
1 //left,最大子数组左端 2 //right,最大子数组右端 3 //n数组arr元素个数 4 const int IntMin = (signed int)0x80000000; 5 int MaxSum(int *arr, int &left, int &right, int n) 6 { 7 if (!arr || n <= 0) { 8 left = -1; 9 right = -1; 10 return IntMin; 11 } 12 13 int maxSum = IntMin; 14 for (int i = 0; i < n; ++i) { 15 int sum = 0; 16 for (int j = i; j < n; ++j) { 17 sum += arr[j]; 18 if (sum > maxSum) { 19 left = i; 20 right = j; 21 maxSum = sum; 22 } 23 } 24 } 25 return maxSum; 26 }
解法二:分治,O(NlogN)
寻找数组A[low…high]的最大子数组时,将数组划分为两个规模尽可能相等的子数组 A[low…mid]和A[mid+1…high]。
A[low…high]的任何连续子数组A[i…j]所处的位置必然是三种情况之一:
(1)完全位于子数组A[low..mid]中,因此low<=i<=j<=mid;
(2)完全位于子数组A[mid + 1..high]中,因此mid<=i<=j<=high;
(3)跨越了中点,因此low<=i<=mid<j<=high;
A[low…high]的一个最大子数组必然是完全位于A[low…mid]中、完全位于A[mid+1…high]中或者跨越中点的所有子数组中和最大者。
1 //left和right是返回值,未初始化,不能直接使用 2 const int IntMin = (signed int)0x80000000; 3 int MaxSumCross(int *arr, int &left, int mid, int &right, int n) 4 { 5 if (arr == NULL) { 6 left = -1; 7 right = -1; 8 return IntMin; 9 } 10 11 int lmaxSum = IntMin; 12 int sum = 0; 13 for (int i = mid; i >= 0; --i) { 14 sum += arr[i]; 15 if (sum > lmaxSum) { 16 left = i; 17 lmaxSum = sum; 18 } 19 } 20 21 int rmaxSum = IntMin; 22 sum = 0; 23 for (int i = mid + 1; i < n; ++i) { 24 sum += arr[i]; 25 if (sum > rmaxSum) { 26 right = i; 27 rmaxSum = sum; 28 } 29 } 30 return lmaxSum + rmaxSum; 31 } 32 33 int MaxSum(int *arr, int &left, int &right, int n) 34 { 35 if (!arr || left > right || n <= 0) { 36 left = -1; 37 right = -1; 38 return IntMin; 39 } 40 if (left == right) 41 return arr[left]; 42 43 //lleft,lright左子数组的左端,右端 44 //rleft,rright右子数组的左端,右端 45 //mleft,mright中间子数组的左端,右端 46 int mid = left + (right - left) / 2; 47 int lleft = left, lright = mid; 48 int rleft = mid + 1, rright = right; 49 int mleft, mright; 50 int leftSum = MaxSum(arr, lleft, lright, n); 51 int midSum = MaxSumCross(arr, mleft, mid, mright, n); 52 int rightSum = MaxSum(arr, rleft, rright, n); 53 54 if (leftSum >= rightSum && leftSum >= midSum) { 55 left = lleft; 56 right = lright; 57 return leftSum; 58 } else if (rightSum >= leftSum && rightSum >= midSum) { 59 left = rleft; 60 right = rright; 61 return rightSum; 62 } else if (midSum >= leftSum && midSum >= rightSum) { 63 left = mleft; 64 right = mright; 65 return midSum; 66 } 67 }
解法三:动态规划,O(N)
1 int MaxSum(int *arr, int &left, int &right, int n) 2 { 3 if (!arr || n <= 0) { 4 left = -1; 5 right = -1; 6 return IntMin; 7 } 8 9 int nAll = arr[n - 1]; 10 int nStart = arr[n - 1]; 11 left = 0; 12 right = n - 1; 13 for (int i = n - 2; i >= 0; --i) { 14 if (nStart < 0) { 15 right = i; 16 nStart = 0; 17 } 18 nStart += arr[i]; 19 if (nStart > nAll) { 20 left = i; 21 nAll = nStart; 22 } 23 } 24 return nAll; 25 }