清晰解题: 寻找最大子数列-Kadane算法

本文参考:
Maximum sub array problem

  • 问题: 给定一个数列,例如【−2, 1, −3, 4, −1, 2, 1, −5, 4】, 求一个连续的数列使得数列内的元素和最大, 示例中最大子数列应该是【4, −1, 2, 1】, 求和值为6。

  • 这个问题是可以衍生到一些变种问题, 如寻找数列中最大乘积序列,且要求序列中,相邻元素间隔不超过限定值等, 常出现在笔试面试编程题中。

  • 该问题最早于1977年提出,但是直到1984年才被Jay Kadane 发现了线性时间的最优解法,所以算法虽然长度很短,但其实并不容易理解。

  • 算法描述:

    • 遍历该数组, 在遍历过程中, 将遍历到的元素依次累加起来, 当累加结果小于或等于0时, 从下一个元素开始,重新开始累加。
    • 累加过程中, 要用一个变量(max_so_far)记录所获得过的最大值
    • 一次遍历之后, 变量 max_so_far 中存储的即为最大子片段的和值。

此处为python 代码 , 变量A 传入数组。

def max_subarray(A):
    max_ending_here = max_so_far = 0
    for x in A:
        max_ending_here = max(0, max_ending_here + x)
        max_so_far = max(max_so_far, max_ending_here)
    return max_so_far
  • 首先, 题目中有一个隐含的设定, 最大子片段是可以为空的, 空片段的和值是0。 这一点必须要明确, 不然会无法理解算法的正确性, 所以当给定的数列中,求和为负数的时候,例如【-2,1, -3】, 算法会返回最大求和值0, 因为默认该数组的最大子片段是一个空序列。

  • 理解此算法的关键在于:

    • 最大子片段中不可能包含求和值为负的前缀。 例如 【-2, 1,4】 必然不能是最大子数列, 因为去掉值为负的前缀后【-2,1】, 可以得到一个更大的子数列 【4】、
    • 所以在遍历过程中,每当累加结果成为一个非正值时, 就应当将下一个元素作为潜在最大子数列的起始元素, 重新开始累加。
    • 由于在累加过程中, 出现过的最大值都会被记录, 且每一个可能成为 最大子数列起始元素 的位置, 都会导致新一轮的累加, 这样就保证了答案搜索过程的完备性和正确性。

你可能感兴趣的:(算法)