最大连续子数组之和的深化

最早是在编程之美上遇到“求数组的连续子数组的最大值”,一道经典动态规划题目。

假设数组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;
    }

你可能感兴趣的:(最大连续子数组之和的深化)