问题描述
定义:最大子数组和:
int 数组中(i, j) i <= j 下标的和最大值
算法简介
一、暴力枚举法
毫无技巧,三重循环,时间复杂度为 O(n^3)
二、优化枚举法
点点技巧,两层循环,时间复杂度为 O(n^2)
三、分而治之法
不断二分,记左边数组的最大子数组和为 s 1 s_1 s1 ,右边数组最大子数组和为 s 2 s_2 s2 ,跨中间区域的最大子数组和为 s 3 s_3 s3
则整个数组的最大子数组和为:
s
=
m
a
x
(
s
1
,
s
2
,
s
3
)
s = max(s_1, s_2, s_3)
s=max(s1,s2,s3)
时间复杂度为 O(nlogn)
最大子数组和函数
int MaxSubArray(int left, int right, int array[])
二分递归求出左右数组的最大子数组和,若 left == right 则返回
跨中间区域的最大子数组和
int CrossingSubArray(int left, int mid, int right, int array[])
跨中间区域的最大子数组和特征:一定包含 array[mid],由此求出
s
3
s_3
s3 ,时间复杂度为 O(n)
四、动态规划法
时间复杂度:O(n)
数学依据:记 D[i] 为以 i 为开头的最大子数组和,一定包含 array[i],则:
s
=
m
a
x
(
D
[
0
,
n
−
1
]
)
s = max(D[0 , n-1])
s=max(D[0,n−1])
递推关系:
D
[
i
]
=
{
D
[
i
+
1
]
+
a
r
r
a
y
[
i
]
D
[
i
+
1
]
>
0
a
r
r
a
y
[
i
]
D
[
i
+
1
]
<
0
D[i] = \begin{cases} D[i+1] + array[i] & D[i+1] >0 \\ array[i] & D[i+1] < 0 \\ \end{cases}
D[i]={D[i+1]+array[i]array[i]D[i+1]>0D[i+1]<0
代码实现
// 此程序用于计算一个int数组中的最大子数组和
// 即int数组中(i, j) i <= j 下标的和最大值
#include <iostream>
#include <cstdio>
using namespace std;
int MaxSubArray(int left, int right, int array[]);
int CrossingSubArray(int left, int mid, int right, int array[]);
int MaxSubArrayDP(int array[], int end, int rec[]);
int main(void) {
int Array[100] = {0}; // 原数组, 简单起见不要超过100
int capacity; // 数组容量
cout << "输入数组的容量:";
cin >> capacity;
for(int i=0; i < capacity; i++) cin >> Array[i];
// 暴力枚举法
// 时间复杂度 O(n^3)
int max1 = -999999999;
for(int i=0;i<capacity;i++) {
for(int j=i;j<capacity;j++) {
int total = 0;
for(int k=i;k<=j;k++) total += Array[k];
max1 = (total > max1) ? total : max1;
}
}
cout << max1 << endl;
// 优化枚举法
// 时间复杂度 O(n^2)
int max2 = -999999999;
for(int i=0;i<capacity;i++) {
int total = 0;
for(int j=i;j<capacity;j++) {
total += Array[j];
max2 = (total > max2) ? total : max2;
}
}
cout << max2 << endl;
// 分而治之法
// 时间复杂度 O(nlogn)
int max3;
max3 = MaxSubArray(0, capacity-1, Array);
cout << max3 << endl;
// 动态规划法
// 时间复杂度 O(n)
int max4;
int Rec[capacity] = {0};
max4 = MaxSubArrayDP(Array, capacity - 1, Rec);
cout << max4 << endl;
return 0;
}
int CrossingSubArray(int left, int mid, int right, int array[]) {
int maxLeft = -999999999;
int maxRight = -999999999;
int addLeft = 0, addRight = 0;
for(int i=mid;i>=left;i--) {
addLeft += array[i];
maxLeft = (addLeft > maxLeft) ? addLeft : maxLeft;
}
for(int i=mid+1;i<=right;i++) {
addRight += array[i];
maxRight = (addRight > maxRight) ? addRight : maxRight;
}
return maxLeft + maxRight;
}
int MaxSubArray(int left, int right, int array[]) {
if(left == right) return array[left];
else {
int s1, s2, s3;
int mid = (left + right) / 2;
s1 = MaxSubArray(left, mid, array); // 左侧数组的最大子数组
s2 = MaxSubArray(mid + 1, right, array); // 右侧数组的最大子数组
s3 = CrossingSubArray(left, mid, right, array); // 跨中间区域的最大子数组
return (s1 > s2 && s1 > s3) ? s1 :
(s2 > s1 && s2 > s3) ? s2 : s3;
}
}
int MaxSubArrayDP(int array[], int end, int rec[]) {
// 数学依据
// 定义 Di 为以i为起点的最大子数组
// 则:若 Di+1 > 0 ,Di = Di+1 + array[i]
// 若 Di+1 < 0 ,Di = array[i]
// 用 Rec[] 记录最大子数组的终止位置
int max = array[end];
int D[end + 1] = {0};
for(int i = end; i >= 0; i--) {
if(i == end) {
D[end] = array[end];
rec[end] = end;
} else {
if(D[i + 1] > 0) {
D[i] = array[i] + D[i + 1];
rec[i] = rec[i + 1];
} else {
D[i] = array[i];
rec[i] = i;
}
max = (D[i] > max) ? D[i] : max;
}
}
return max;
}