算法专题 之 动态规划

Posted JAVA万维猿圈

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法专题 之 动态规划相关的知识,希望对你有一定的参考价值。

由于动态规划算法具有时间效率较高,代码量较少,可以考察思维能力、抽象能力以及灵活度,该算法的身影常常出现在面试、笔试或者竞赛中,今天对该算法进行总结并对面试等场合中常出现的题目进行分析。

1、动态规划定义与理解

    动态规划(Dynamic Programming,简称DP),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解。(上述定义来自百度百科)

    个人理解,动态规划的核心包含3个重要环节:

  1. 记录求解过程:记忆化存储每个子问题的解,以便下次需要同一个子问题解之时直接查询记录;

  2. 状态转移方程:整体问题最优解取决于子问题的最优解,因此需要得到子问题与最终问题间的关系;

  3.  边界问题:状态转移方程是一个递推式,因此需要找到递推终止的条件。

    简单点说,动态规划思想就像《红楼梦》第六十二回提到的经典谚语“ ‘大事化为小事,小事化为没事’,方是兴旺之家”的处事方式,对于很多需要求解最优解的场合,动态规划方是解题之道。

动态规划求解步骤:

  1. 判题题意是否为找出一个问题的最优解

  2. 把原问题分解成若干个子问题,分析最优子结构与最终问题之间的关系,从而得到状态转移方程

  3. 确定底层边界问题,例如最小的前几个f(n)的值;

  4. 求解问题,通常使用数组进行迭代求出最优解。

2、递归、贪心算法、分治策略以及动态规划的比较

  1. 递归:将原问题归纳为更小的、相似的子问题,递归的过程中存在子问题的重复计算,耗费更多的时间和空间。

  2. 贪心算法:依赖于当前已经做出的所有选择,采用自顶向下(每一步根据策略得到当前一个最优解,保证每一步都是选择当前最优的)的解决方法,不能保证求出最优解,因此不能用来求最大或最小解问题。

  3. 分治策略:将原问题分解为若干个规模较小、相互独立、类似于原问题的子问题,自顶向下递归求解这些子问题,然后再合并这些子问题的解来建立原问题的解。

  4. 动态规划:用于解决子问题有重复求解的情况,子问题之间是不独立,既可以用递归实现,也可以用迭代实现,动态规划能求出问题的最优解。

3、面试中常见的动态规划问题

    选取字符串、数组以及树三种类别常见的动态规划题目进行分析和编码。

题目1:最长回文子串

题目描述:给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为1000。

示例 1:输入: "babad"      输出: "bab"    注意: "aba"也是一个有效答案。

示例 2:输入: "cbbd"        输出: "bb"

解法1:暴力求解,求出该字符串的每一个子串,再判断子串是否是回文串,找到最长的那个。其中求出每个子串的时间复杂度为O(n^2),判断是否为回文串的复杂度为O(n),两者是相乘关系,所以整个算法的时间复杂度为O(n^3),空间复杂度:O(1)。

解法2:中心扩展法依次去求得每个字符的最长回文,注意每个字符有奇数长度的回文串和偶数长度的回文串两种情况,记录最长回文的始末位置即可。时间复杂度为 O(n^2)。 注:下面代码可左右滑动查看

class Solution { int start = 0, end = 0; public String longestPalindrome(String s) { int len = s.length(); if (len <= 1) return s; char[] chars = s.toCharArray(); for (int i = 0; i < len; i++) { helper(chars, i, i); helper(chars, i, i + 1); } return s.substring(start, end + 1); } private void helper(char[] chars, int l, int r) { while (l >= 0 && r < chars.length && chars[l] == chars[r]) { --l; ++r; } if (end - start< r - l - 2) { start= l + 1; end = r - 1; } }}

解法3:动态规划

   定义dp[i][j] 的意思为字符串区间[i, j] 是否为回文串,那么我们分三种情况:

(1)当 i == j 时,那么毫无疑问 dp[i][j] = true;

(2)当 i + 1 == j 时,那么 dp[i][j] 取决于 s[i] == s[j];

(3)当 i + 1 < j 时,那么 dp[i][j] 取决于 dp[i + 1][j - 1] && s[i] == s[j]。

    时间复杂度为O(n^2),空间复杂度为O(n^2)。注:下面代码可左右滑动查看

class Solution { public String longestPalindrome(String s) { int len = s.length(); if (len <= 1) return s; int st = 0, end = 0; char[] chars = s.toCharArray(); boolean[][] dp = new boolean[len][len]; for (int i = 0; i < len; i++) { dp[i][i] = true; for (int j = 0; j < i; j++) { if (j + 1 == i) { dp[j][i] = chars[j] ==chars[i]; } else { dp[j][i] = dp[j + 1][i - 1]&& chars[j] == chars[i]; } if (dp[j][i] && i - j> end - st) { st = j; end = i; } } } return s.substring(st, end + 1); }}

题目2:连续子数组的最大和 

题目描述:例如:{6,-3,-2,7,-15,1,2,2},连续子向量的最大和为8(从第0个开始,到第3个为止)。给一个数组,返回它的最大连续子序列的和

解法1:暴力求解,首先找出所有子数组,然后求出子数组的和,在所有子数组的和中取最大值。时间复杂度为O(n^3),求解过程中存在子数组重复计算的问题。

解法2:动态规划

动态规划状态转移方程:

(1)当dp[i - 1]> 0 时,那么dp[i] = dp[i - 1] + nums[i];

(2)当 dp[i - 1]<= 0 时,那么 dp[i] = nums[i]

时间复杂度为O(n),空间复杂度也为O(n)。注:下面代码可左右滑动查看

public int maxSubArray (int[] nums) { if (nums.length == 0) return 0; if (nums.length == 1) return nums[0]; int[] dp = new int[nums.length]; dp[0] = nums[0]; int max = dp[0]; for (int i = 1; i < dp.length; i++) { if (dp[i - 1] > 0) { dp[i] = dp[i - 1] + nums[i]; } else { dp[i] = nums[i]; } max = Math.max(dp[i],max); } return max;}

题目3:不同的二叉搜索树

题目描述:给定一个整数 n,求以 1 ... n 为节点组成的二叉搜索树有多少种?

示例 1:输入: 3      输出: 5;

    解释:给定 n = 3, 一共有 5 种不同结构的二叉搜索树

1 3 3 2 1
\ / / / \ \
3 2 1 1 3 2
/ / \ \
2 1 2 3

求解方法:动态规划

    假设DP(n)表示n个节点组成不同二叉搜索树的种类数,分别考虑1到n作为根结点的情况。

(1) 根节点为1,则左子树必定为空,右子树为2...n个节点,那么种类数为1*DP[n-1],也可以表示为DP[0]*DP[n-1]。

(2) 根节点为2,则左子节点为1,右子树为3...n个节点,即DP[1]*DP[n-2]

(3) 根节点为3,则左子节点为1,2,右子树为4...n个节点,即DP[2]*DP[n-3]

......

每个根有DP[n-1]种情况, 根结点2到n-1时,每个根有DP[左边剩下数字] * DP[右边剩下数字] 种情况。

状态转移方程:DP(n) = DP(0)*DP(n-1)+DP(1)*DP(n-2)+...+DP(n-1)*DP(0)   注:下面代码可左右滑动查看

class Solution { public int numTrees(int n) { int[] DP = new int[n + 1]; DP[0] = DP[1] = 1; for (int i = 2; i <= n; i++) { for (int j = 1; j <= i; j++) { DP[i] += DP[j - 1] * DP[i - j]; } } return DP[n]; }}


以上是关于算法专题 之 动态规划的主要内容,如果未能解决你的问题,请参考以下文章

PAT刷题之动态规划专题

算法面试专题-动态规划

动态规划入门专题合集

LeetCode日记——算法动态规划专题——斐波那契数列

专题三-动态规划算法总结

动态规划专题