T(n) = O(n) 的最大子数组问题解法

题目见算法导论 4.1-5,这篇文章作为笔记,不阐述原理。

import random

__author__ = 'Administrator'

LENGTH = 500
base = []

for i in range(0, LENGTH * 2):
    base.append(random.randint(-1 * LENGTH, LENGTH))

print(base)
bsa_i = 0
bsa_j = 1
bsa = base[0]
bord = base[0]
bord_i = 0

for i in range(1, len(base)):
    if bord < 0:
        bord_i = i
        bord = base[i]
    else:
        bord += base[i]
    if bord >= bsa:
        bsa_i = bord_i
        bsa_j = i + 1
        bsa = bord
print(bsa_i, bsa_j, bsa)
print(base[bsa_i: bsa_j])

# verify:
bsa = -1 * LENGTH - 1
for i in range(0, len(base)):
    for j in range(i + 1, len(base) + 1):
        it = 0
        for k in range(i, j):
            it += base[k]
        if it > bsa:
            bsa = it
            bsa_i = i
            bsa_j = j
print(bsa_i, bsa_j, bsa)
print(base[bsa_i: bsa_j])

指针 i 右移时,记录以 i + 1 为右边界的最大子数组 bord,初始为 bord[0, 1],当 sum bord < 0 时, bord 左边界替换为 i 。 这种求法意为去掉左边的所有和为负的子数组,剩下的便是以 i + 1 为右边界的最大子数组。正确性可以这样验证:

 

反证法

原命题: bord[i, j] 中,如果不存在 k 属于 [i, j] 使得 sum A[i, k] <= 0, 这时不存在 l 属于 [i, j] 使得 sum B[l, j] >= sum bord[i, j]。

假设:bord[i, j] 中,如果不存在 k 属于 [i, j] 使得 sum A[i, k] <= 0, 这时存在 l 属于 [i, j] 使得 sum B[l, j] >= sum bord[i, j]。

如果 存在 l 属于 [i, j] 使得 sum B[l, j] >= sum bord[i, j],那么 sum C[i, l] = sum bord[i, j] - sum B[l, j] <= 0

令 l = k, 则假设矛盾不成立,原命题正确性得证。

 

bsa 数组记录的是 [0, i] 中的最大子数组,而 border 是 右边界为 i + 1 的最大子数组,若 sum border > sum bsa,则 border 成为 bsa。这样在一次遍历中,就完成了对整个数组的最大子数组 bsa 的查找,时间复杂度为 O(n),之后的验证算法采用三重循环暴力求解,时间复杂度为O(n ^ 3)。而三次方级别的复杂度对于一个稍大的 n 来讲,几乎就是一个无用的算法,所以设计高效的子数组求法是具有很强的实际意义的。

posted @ 2015-05-17 08:23  =风间苍月=  阅读(205)  评论(0编辑  收藏  举报