更多内容,欢迎关注微信公众号:全菜工程师小辉。公众号回复关键词,领取免费学习资料。程序员
动态规划算法一直是面试手撕算法中比较有挑战的一种类型。不少的分配问题或者调度问题实际上均可能用动态规划进行解决。(固然,若是问题的规模较大,有时候会抽象模型使用动归来解决,有时候则能够经过不断迭代的几率算法解决查找次优解)面试
因此,动归很重要,至少算法思想很重要。算法
经过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。动态规划经常适用于有重叠子问题和最优子结构性质的问题。 > 最优子结构:当问题的最优解包含了其子问题的最优解时,称该问题具备最优子结构性质。微信
> 重叠子问题:在用递归算法自顶向下解问题时,每次产生的子问题并不老是新问题,有些子问题被反复计算屡次。动态规划算法正是利用了这种子问题的重叠性质,对每个子问题只解一次,然后将其解保存在一个表格中,在之后尽量多地利用这些子问题的解。学习
不理解不用怕,结合后面题目来理解这些概念。这些概念彻底是已经会动归的人来总结出来的,因此先理解动归,而后再来看这些文绉绉的归纳。code
共同点:
两者都要求原问题具备最优子结构性质,都是将原问题分而治之,分解成若干个规模较小(小到很容易解决)的子问题。而后将子问题的解合并,造成原问题的解。递归
不一样点:ip
全部的动态规划问题均可以经过多层嵌套循环遍历全部的可能,将符合条件的个数统计起来。只是时间复杂度是指数级的,因此不推荐。leetcode
> 上面提到的三个问题是动态规划里很常见的题目,题目内容能够百度查看一下。篇幅缘由,本文后边只讲解前两道题get
例1: Climbing Stairs(爬楼梯问题)
leetcode原题:你正在爬一个有n个台阶的楼梯,每次只能上1个或者2个台阶,那么到达顶端共有多少种不一样的方法?
class Solution { int climbStairs(int n) { if (n <= 2) { return n; } else { return climbStairs(n - 1) + climbStairs(n - 2); } } }
> 递归的时间复杂度是由递归层数和最优子结构的个数决定的。这里的阶梯数是 N ,最优子结构个数是2。若是想象成一个二叉树,那么就能够认为是一个高度为N-1,节点个数接近2的N-1次方的树,所以此方法的时间复杂度能够近似的看做是O(2<sup>N</sup>) 。
class Solution { private Map<integer, integer> map = new HashMap<>(); int climbStairs(int n) { if (n <= 2) { return n; } else if (map.containsKey(n)) { return map.get(n); } else { int value = climbStairs(n - 1) + climbStairs(n - 2); map.put(n, value); return value; } } }
class Solution { int climbStairs(int n) { if (n <= 2) { return n; } // 边界条件 int a = 1; int b = 2; int result = 0; // 最优子结构与最终问题之间的关系 for (int i = 3; i <= n; i++) { result = a + b; a = b; b = result; } return result; } }
> 空间复杂度O(1), 时间复杂度O(N)
例2: Making change using the fewest coins(最少找零钱问题)
Google面试题:假设你是一家自动售货机制造商的程序员。你的公司正设法在每一笔交易 找零时都能提供最少数目的硬币以便工做能更加简单。已知硬币有四种(1美分,5美分,10美分,25美分)。假设一个顾客投了1美圆来购买37美分的物品 ,你用来找零的硬币的最小数量是多少?
class Solution { Set<integer> coinSet = new HashSet<integer>() { { add(1); add(5); add(10); add(25); } }; int getFewestCoins(int n) { if (n < 1) { return 0; } if (coinSet.contains(n)) { return 1; } int minCoins = n; int numCoins = Integer.MAX_VALUE; for (int coin : coinSet) { if (n >= coin) { // 若是要计算的n小于单个硬币金额,则不能出如今状态转移方程中 numCoins = 1 + getFewestCoins(n - coin); } // 更新最小值 if (numCoins < minCoins) { minCoins = numCoins; } } return minCoins; } }
备忘录算法:
就是将递归里计算的中间变量都保存在一个哈希表,代码略。
动态规划:
自底向上,从找零数等于1开始往上迭代,参考最优子结构,记录下来最少硬币数。一直迭代到实际要求。
class Solution { Set<integer> coinSet = new HashSet<integer>() { { add(1); add(5); add(10); add(25); } }; int getFewestCoins(int n) { int[] list = new int[n + 1]; List<integer> subCal = new ArrayList<>(); for (int i = 0; i <= n; i++) { // 边界 if (i <= 1) { list[i] = i; continue; } for (int cent : coinSet) { if (i >= cent) { subCal.add(list[i - cent] + 1); } } list[i] = Collections.min(subCal); subCal.clear(); } return list[n]; } }
更多内容,欢迎关注微信公众号:全菜工程师小辉。公众号回复关键词,领取免费学习资料。
</integer></integer></integer></integer></integer></integer,>