最早是在编程之美上遇到“求数组的连续子数组的最大值”,一道经典动态规划题目。
假设数组arr[]长度是len,dp[i]表示[ i , len -1 ]的连续子数组最大值,那么则有以下规律:
dp[i] = max( arr[i], arr[i] + start(i+1), dp(i+1)),其中 start(i+1)表示从arr[i+1]开始的连续子数组最大值。
动态规划常用递归解决,但是递归往往需要一个空间来记录中间的计算结果,所以写成写成递推的方式更加简明。递推的方向是和动态规划的方向相反的,所以需要逆序遍历数组。
int maxSum(int[] array) {
if (array == null || array.length == 0) {
return 0;
}
int len = array.length;
int sum = array[len - 1];
int cur = array[len - 1];
for (int i = len - 2; i >= 0; i--) {
if (cur > 0) {
cur += array[i];
} else {
cur = array[i];
}
if (cur > sum) {
sum = cur;
}
}
return sum;
}
细察动态规划转移方程是怎样写成代码的,max( a,b, c ) = max( max(a,b) ,c)
其实顺序遍历数组也可以,与之对应的动态规划的方向则是逆序的。
除此之外,还有暴力和分而治之的算法。
深化:记录连续最大子数组的全部下标,要求复杂度是线性。
List> maxSumIndex(int[] array) {
if (array == null || array.length == 0) {
Collections.emptyList();
}
int len = array.length;
int end = len - 1;
List> list = new ArrayList<>();
list.add(Stream.of(end, end).collect(Collectors.toList()));
int sum = array[len - 1];
int cur = array[len - 1];
for (int i = len - 2; i >= 0; i--) {
if (cur > 0) {
cur += array[i];
} else {
cur = array[i];
end = i;
}
if (cur >= sum) {
if (cur > sum) {
sum = cur;
list.clear();
}
list.add(Stream.of(i, end).collect(Collectors.toList()));
}
}
return list;
}
选几个特殊的测试用例:
[100,-500, 50 ,50,-500 ,100]
[-1, -1, -1]
[ 0, 0,0 ]
[100, -1000, 0, 0, 50, 50, 0, 0, -1000, 100]
其中第三、四个case是不通过的,因为最大连续子数组之后有0。
假设cur[i]= 0,那么说明arr[i] = 0,此时endIndex有多个,需要用list记录。
List> maxSumIndex(int[] array) {
if (array == null || array.length == 0) {
Collections.emptyList();
}
int len = array.length;
int end = len - 1;
List> list = new ArrayList<>();
list.add(Stream.of(end, end).collect(Collectors.toList()));
List endList = Stream.of(end).collect(Collectors.toList());
int sum = array[len - 1];
int cur = array[len - 1];
for (int i = len - 2; i >= 0; i--) {
if (cur > 0) {
cur += array[i];
} else if (cur == 0) {
cur = array[i];
endList.add(i);
} else {
cur = array[i];
endList.clear();
endList.add(i);
}
if (cur >= sum) {
if (cur > sum) {
sum = cur;
list.clear();
}
for (Integer index : endList) {
list.add(Stream.of(i, index).collect(Collectors.toList()));
}
}
}
return list;
}
2019-03-05再读,写出一个对称版的解答。
public int maxSum(int[] arr) {
final int len = arr.length;
int result = arr[0];
int cur = arr[0];
for (int i = 1; i < len; i++) {
if (cur > 0) {
cur += arr[i];
} else {
cur = arr[i];
}
if (cur > result) {
result = cur;
}
}
return result;
}