畅游面试中的动态规划套路-斐波那契数列系列

索引链接:畅游面试中的动态规划套路

在这里插入图片描述

斐波那契数

题目链接:509. Fibonacci Number

题解链接:畅游面试中的动态规划套路-斐波那契数列系列之斐波那契数列

方法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 不进行rob f[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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值