【算法题】最大连续子序和

一道 LeetCode 的动态规划题的分析。算法

题目描述

题目来源 leetCode——53.最大子序和数组

给定一个整数数组 nums ,找到一个具备最大和的连续子数组(子数组最少包含一个元素),返回其最大和。缓存

示例:bash

输入: [-2,1,-3,4,-1,2,1,-5,4],
输出: 6
解释: 连续子数组 [4,-1,2,1] 的和最大,为 6。
复制代码

进阶: 若是你已经实现复杂度为 O(n) 的解法,尝试使用更为精妙的分治法求解。数据结构

动态规划

在分析如何使用动态规划求解该问题前,咱们先简单了解下什么是什么是动态规划(DP)。函数

动态规划的适合解决的问题应该符合 “一个模型三个特征”ui

一个模型

一个模型指的是 多阶段决策最优解模型。指的是要经历多个决策阶段,每一个决策对应多个状态。而后咱们从中找出一组可以产生最优解的决策序列。spa

三个特征

  1. 最优子结构

后面的决策能够经过前面的决策推导出。 好比说最经典的0-1背包问题,咱们依次把物品放入背包(或不放入)背包。当进行到第 n 次决策(是否将第 n 个物品放入书包)时,前面的第 n - 1 已经决定好的多种决策(前面不一样决策序列最终在 n - 1 次决策时给出了全部能够达到的重量),不会由于第 n 次的决策发生改变。3d

  1. 无后效性

一旦某个阶段 a 的决策结束后,后面阶段进行决策时,就不用考虑到阶段 a 是如何推导出来的。咱们只须要用到它的最终获得的决策序列。code

  1. 重复子问题

不一样的决策序列,在到达第 n 次决策结束后,可能会产生重复的状态。好比假设依次要放入背包的物品的重量分别为 二、四、二、3。 咱们进入决策是否放入第3个物品的阶段时,就会出现 “放入第一、3个物品和只放入第2个物品的两组决策序列达到相同的重量”。这就是重复的状态,可能会发生也可能不会发生。

动态规划的两种解题思路

1. 状态转移表法

动态规划能解决的问题,均可以用回溯法的暴力搜索解决。因此咱们能够先用简单的回溯算法去试着去解决,从中找到规律,画出递归树。

当咱们发现重复子问题时,一是可使用 回溯+“备忘录” 的方法。所谓备忘录,就是咱们会把 f(n) 的结果用散列表保存起来(回溯常常用到递归函数),当又一次调用 f(n) 时,就直接取出缓存起来的结果,以减小重复计算。二是这里说的 状态表转移表法

一般状态表是二维度的。每行表明着每个阶段决策后的多个状态。这个二维数组会一行一行地进行填充,直到达到知足结束状态的状况(好比0-1背包问题就是重量恰好达到背包最大承重,或者n个物品都进行了决策,即完成了第 n 次决策)。这时咱们只须要从最终的阶段找出最终结果便可(背包问题是从后往前找出一个重量最大的值)

2. 状态转移方程法

关键在于找出 状态转移方程。根据最优子结构,写出递归公式,也就是所谓的状态转移方程。

动态规划解法

那么咱们开始着手分析问题了。

首先咱们试着用 状态转移表法 来作这道题。

以 [-2,1,-3,4,-1,2,1,-5,4] 为例进行分析,咱们画个递归树分析一下。

(i, sum)。i 表明 第 i 个阶段的决策,具体作的决策是:是选择当前的元素为新的子数组的起点,仍是让当前元素做为原来子数组的后继数组元素。具体逻辑以下图:

每一个阶段咱们都会拿到决策后的两种连续子数组的和,咱们会把它们和上一次阶段的最大和比较,取出最大的值。

这里要注意的是,当咱们要选择决策后的两种状况的其中一种状况和大的状况,进行下一次遍历,另外一种状况是不须要进行下次的递归的。由于总和小的状况下的数组,和后面的数组相加时,必然比总和大的状况的总和要小,因此不须要进行接下来的递归。

js 代码实现

var maxSubArray = function(nums) {
    // 动态规划
    let len = nums.length;
    let max = nums[0];
    let prevSum = nums[0];

    for (let i = 1; i < len; i++) {
        prevSum = Math.max(nums[i], prevSum + nums[i]);
        max = Math.max(max, prevSum);
    }
    return max;
};
复制代码

参考

相关文章
相关标签/搜索