《剑指offer31》:连续子数组的最大和

这是《剑指offer》系列的第一篇,这个系列的文章主要用于记录看书时的一些感想,做个记录。

 

题目

输入一个整型数组,数组里有正数也有负数。数组中一个或连续的多个整数组成一个子数组。求所有子数组的和的最大值。要求时间复杂度为O(n)。例:

《剑指offer31》:连续子数组的最大和

思路

这里采用动态规划的思想:

  • 第一步:做一个选择,基于这个选择产生一个或多个待解的子问题

这里用函数 《剑指offer31》:连续子数组的最大和 表示以第 《剑指offer31》:连续子数组的最大和 个数字结尾的子数组的最大和,其中 《剑指offer31》:连续子数组的最大和。注意到函数 《剑指offer31》:连续子数组的最大和 的不同取值构成了原题可能解的全集。例:当 《剑指offer31》:连续子数组的最大和 的时候,有 《剑指offer31》:连续子数组的最大和  。

这里我们选择子数组的结束位置 《剑指offer31》:连续子数组的最大和 ,并且假定这个选择是最优解。即:我们假设结束于下标为 《剑指offer31》:连续子数组的最大和 的子数组的最大和是原数组中所有子数组的最大和。

选择了结束位置之后,数组下标 6 的右边不用再进行考虑。那么基于 《剑指offer31》:连续子数组的最大和 产生子问题 《剑指offer31》:连续子数组的最大和 ,意义是结束于下标为  《剑指offer31》:连续子数组的最大和 的子数组的最大和。

  • 第二步:证明问题具有最优子结构性质,并得到递归式

使用“剪切 - 粘贴”反证法,证明在 《剑指offer31》:连续子数组的最大和 的条件下问题具有最优子结构性质:

假设 《剑指offer31》:连续子数组的最大和 不是数组区间 《剑指offer31》:连续子数组的最大和 内结束于下标为 《剑指offer31》:连续子数组的最大和 的子数组的最大和,那么我们将 《剑指offer31》:连续子数组的最大和 从 《剑指offer31》:连续子数组的最大和 中剪切去掉,并把最优解 《剑指offer31》:连续子数组的最大和 粘贴其中得到  《剑指offer31》:连续子数组的最大和, 则有 《剑指offer31》:连续子数组的最大和,与“ 《剑指offer31》:连续子数组的最大和 是最优解”矛盾,故原假设不成立,得证。

这里稍微有点绕,如下图:

《剑指offer31》:连续子数组的最大和

这里我们证明的是: 《剑指offer31》:连续子数组的最大和 区间在 《剑指offer31》:连续子数组的最大和 的时候一定会被《剑指offer31》:连续子数组的最大和 区间包含。

注:这里约束 《剑指offer31》:连续子数组的最大和 的意义在于,当子问题的解是负数的时候,即使它是子问题能达到的最大值,也会拖了原问题的后腿,还不如直接等于 《剑指offer31》:连续子数组的最大和 。例如 《剑指offer31》:连续子数组的最大和 的子问题 《剑指offer31》:连续子数组的最大和​ ​​​​​​,那结束于下标为 《剑指offer31》:连续子数组的最大和 的子数组的最大和应该等于 《剑指offer31》:连续子数组的最大和 本身,即 《剑指offer31》:连续子数组的最大和 。这是来自于我们定义这个函数时隐含的约束 

由此我们证明了原问题的最优解 “ 结束于下标为 《剑指offer31》:连续子数组的最大和 的子数组的最大和 ” 包含了它的子问题 “ 结束于下标为 《剑指offer31》:连续子数组的最大和 的子数组的最大和 ” 的最优解。我们称这个问题具有最优子结构。

得到以下递推式:

《剑指offer31》:连续子数组的最大和

  • 第三步:证明问题具有重叠子问题性质

如果递归算法反复求解相同的子问题,我们称最优化问题具有重叠子问题性质,这里我们采用子问题图来可视化子问题之间的依赖关系,其中节点代表子问题,弧代表调用关系:

《剑指offer31》:连续子数组的最大和

例如规模为4的子问题在求解时会调用规模为3、2和1的子问题,而处理规模为3的子问题的时候又会调用规模为2和1的子问题,造成高昂的计算代价。

  • 第四步:使用动态规划进行求解

动态规划求解分为带备忘录的自顶向下法带表格的自底向上法。

以下附上剑指offer提供的自底向上法:

《剑指offer31》:连续子数组的最大和

结语

动态规划的题目常见,做多了题,有时候稀里糊涂地套上去试一下能做出来。但一直不懂背后的原理。这篇主要是用《算法导论》里面的分析手法分析了下《剑指offer》里的题,得到了些粗浅的看法,以后还要多加练习。

 

Reference:

《算法导论》p216

《剑指offer》p171