索引链接:畅游面试中的动态规划套路
斐波那契数
题解链接:畅游面试中的动态规划套路-斐波那契数列系列之斐波那契数列
方法1:暴力递归
- 经典的种子题
public int fib(int n) {
if(n==0) return 0;
if(n==1) return 1;
return fib(n-1)+fib(n-2);
}
方法2:自顶向下记忆化递归(Top-down)
Integer[] memo;
public int fib(int n) {
memo = new Integer[n+1];
return recursive(n);
}
private int recursive(int num){
if(memo[num]!=null) return memo[num];
if(num==0) return 0;
if(num==1) return 1;
return memo[num] = ( fib(num-1)+fib(num-2));
}
方法3:自底向上填表DP(Bottom-up)
public int fib(int n) {
if(n<2) return n;
int[] f= new int[n+1];
f[0] = 0;
f[1]= 1;
for(int i = 2;i<=n;i++) f[i] =f[i-1]+f[i-2];
return f[n];
}
爬楼梯
题目链接:70. Climbing Stairs
题解链接:畅游面试中的动态规划套路-斐波那契数列系列之爬楼梯
方法1:暴力递归
public int climbStairs(int n) {
if(n==1) return 1;
if(n==2) return 2;
return climbStairs(n-1)+climbStairs(n-2);
}
方法2:自顶向下记忆化递归(Top-down)
int[] memo ;
public int climbStairs(int n) {
memo = new int[n+1];
return helper(n);
}
private int helper(int n ){
if(memo[n]!=0) return memo[n];
if(n==1) return 1;
if(n==2) return 2;
return memo[n] =(helper(n-1)+helper(n-2));
}
方法3:自底向上填表DP(Bottom-up)
public int climbStairs(int n) {
if(n<=2) return n;
int[] dp = new int[n+1];
dp[1] = 1;
dp[2] =2;
for(int i = 3;i<=n;++i) dp[i] = dp[i-2]+dp[i-1];
return dp[n];
}
分解因子问题
Number factors
定义
因子:假如整数n除以m,结果是无余数的整数,那么我们称m就是n的因子。
质因子:在数论里,某一正整数的质因子指能整除该数的**质数**整数。
完数:一个数的因子之和等于它本身,则该数为完数。
方法1:暴力递归
public int countWays(int n) {
if (n == 0) return 1;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
int s1 = countWays(n - 1);
int s3 = countWays(n - 3);
int s4 = countWays(n - 4);
return s1 + s3 + s4;
}
方法2:自顶向下记忆化递归(Top-down)
Integer[] memo;
public int countWays(int n) {
memo = new Integer[n + 1];
return helper(n);
}
private int helper(int n) {
if (memo[n] != null) return memo[n];
if (n == 0) return 1;
if (n == 1) return 1;
if (n == 2) return 1;
if (n == 3) return 2;
int s1 = countWays(n - 1);
int s3 = countWays(n - 3);
int s4 = countWays(n - 4);
return memo[n] = s1 + s3 + s4;
}
方法3:自底向上填表DP(Bottom-up
public int countWays(int n) {
int[] f = new int[n + 1];
f[0] = f[1] = f[2] = 1;
f[3] = 2;
for (int i = 4; i <= n; i++) {
f[i] = f[i - 1] + f[i - 3] + f[i - 4];
}
return f[n];
}
最小跳跃次数
Minimum jumps to reach the end
方法1:暴力递归
int INF = Integer.MAX_VALUE / 2;
public int countMinJumps(int[] jumps) {
return helper(jumps, 0);
}
public int helper(int[] jumps, int currIdx) {
if (currIdx == jumps.length - 1) return 0;
if (jumps[currIdx] == 0) return INF;
int total = INF;
int start = currIdx + 1, end = currIdx + jumps[currIdx];
while (start < jumps.length && start <= end) {
int min = helper(jumps, start++);
if (min != INF) total = Math.min(total, min + 1);
}
return total;
}
方法2:自顶向下记忆化递归(Top-down)
int INF = Integer.MAX_VALUE / 2;
Integer[] memo;
public int countMinJumps(int[] jumps) {
memo = new Integer[jumps.length];
return helper(jumps, 0);
}
public int helper(int[] jumps, int currIdx) {
if (currIdx == jumps.length - 1) return 0;
if (jumps[currIdx] == 0) return INF;
if (memo[currIdx] != null) return memo[currIdx];
int total = INF;
int start = currIdx + 1, end = currIdx + jumps[currIdx];
while (start < jumps.length && start <= end) {
int min = helper(jumps, start++);
if (min != INF) total = Math.min(total, min + 1);
}
return memo[currIdx] = total;
}
方法3:自底向上填表DP(Bottom-up)
int INF = Integer.MAX_VALUE / 2;
public int countMinJumps(int[] jumps) {
int n = jumps.length;
int[] f = new int[n];
Arrays.fill(f, INF);
f[0] = 0;
for (int start = 0; start < n - 1; start++) {
for (int end = start + 1; end < jumps[start] + start && end < n; end++) {
f[end] = Math.min(f[end], f[start] + 1);
}
}
return f[n - 1];
}
打家劫舍
题目链接:198. House Robber
题解链接:畅游面试中的动态规划套路-斐波那契数列系列之打家劫舍
方法1:暴力递归
public int rob(int[] nums) {
return helper(nums, nums.length - 1);
}
public int helper(int[] nums, int i) {
if (i < 0) return 0;
int steal = nums[i] + helper(nums, i - 2);
int non_steal = helper(nums, i - 1);
return Math.max(steal, non_steal);
}
方法2:自顶向下记忆化递归(Top-down)
从前往后跳
Integer[] memo;
public int rob(int[] nums) {
memo = new Integer[nums.length];
return helper(nums, 0);
}
/**
* @param nums
* @param i 表示当前的房间号
* @return
*/
public int helper(int[] nums, int i) {
if (i >= nums.length) return 0;
if (memo[i] != null) return memo[i];
int steal = nums[i] + helper(nums, i + 2);
int non_steal = helper(nums, i + 1);
return memo[i] = Math.max(steal, non_steal);
}
从后往前跳
Integer[] memo;
public int rob(int[] nums) {
memo = new Integer[nums.length];
return helper(nums, nums.length - 1);
}
public int helper(int[] nums, int i) {
if (i < 0) return 0;
if (memo[i] != null) return memo[i];
int steal = nums[i] + helper(nums, i - 2);
int non_steal = helper(nums, i - 1);
return memo[i] = Math.max(steal, non_steal);
}
方法3:自底向上填表DP(Bottom-up)
f[i][0]
表示对于当前的房间号i-1
不进行robf[i][1]
表示进行rob- 如果不rob的时候,其值是前一个房间号的 rob 和 不rob的最大值
- 如果rob的时候,其值是拿到当前房间的价值 + 前一个房间没有rob的的 价值
f[i - 1][0]
public int rob(int[] nums) {
int n = nums.length;
int[][] f = new int[n + 1][2];
for (int i = 1; i <= n; i++) {
f[i][0] = Math.max(f[i - 1][0], f[i - 1][1]);
f[i][1] = nums[i - 1] + f[i - 1][0];
}
return Math.max(f[n][0], f[n][1]);
}
Space O(1)
public int rob(int[] nums) {
int prev1 = 0, prev2 = 0; //表示rob与否的价值
for (int v : nums) {
int t = prev1;
prev1 = Math.max(prev1, prev2);
prev2 = t + v;
}
return Math.max(prev1, prev2);
}