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 来讲,几乎就是一个无用的算法,所以设计高效的子数组求法是具有很强的实际意义的。