JavaScript算法 + leetcode - 动态规划 + 贪心算法+ 回溯算法

260 阅读2分钟

本文记录动态规划算法设计思想

动态规划

  • 动态规划是算法设计中的一种设计方法。

  • 它将一个问题分解为相互重叠的子问题,通过反复求解子问题,来解决原来的问题。

动态规划 vs 分而治之

动态规划是分解为相互重叠的子问题,如斐波那契数列,F(n) = F(n-1) + F(n-2)。

分而治之分解出来的子问题是独立的子问题。如翻转二叉树。

leetcode题目

70. 爬楼梯

/**
 * @param {number} n
 * @return {number}
 */
var climbStairs = function(n) {
    if (n <= 2) return n;
    let dp0 = 1, dp1 = 1;
    for (let i = 2; i <= n; i++) {
        const temp = dp0;
        dp0 = dp1;
        dp1 = dp1 + temp;
    }
    return dp1;
}

198. 打家劫舍

/**
 * @param {number[]} nums
 * @return {number}
 */
var rob = function(nums) {
    if (nums.length === 0) return 0;
    let dp0 = 0, dp1 = nums[0];
    for (let i = 2; i <= nums.length; i++) {
        const dp2 = Math.max(dp0 + nums[i - 1], dp1);
        dp0 = dp1;
        dp1 = dp2;
    }
    return dp1;
}

53. 最大子序和

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
  let dp = [];
  dp[0] = nums[0];
  for (let i = 1; i < nums.length; i++) {
    dp[i] = Math.max(dp[i - 1] + nums[i], nums[i]);
  }
  return Math.max(...dp);
}

以上方法通过数组存储和,空间复杂度为O(n)

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxSubArray = function(nums) {
    let sum = 0;
    let ans = nums[0];
    for (let num of nums) {
        sum = sum > 0 ? sum + num : num;
        ans = Math.max(sum, ans);
    }
    return ans;
}

通过优化后,空间复杂度降为O(1)

312. 戳气球

/**
 * @param {number[]} nums
 * @return {number}
 */
var maxCoins = function(nums) {
    if (!nums.length === 0) return 0;
    nums = [1, ...nums, 1];
    const len = nums.length;
    let dp = Array.from(Array(len), n => Array(length).fill(0));
    for (let i = len - 2; i >= 0; i--) {
        for (let j = i + 2; j < len; j++) {
            let max = 0;
            for (let k = i + 1; k < j; k++) {
                const temp = dp[i][k] + dp[k][j] + nums[i] * nums[j] * nums[k];
                max = Math.max(temp, max);
            }
            dp[i][j] = max;
        }
    }
    return dp[0][len - 1];
}

贪心算法

  • 贪心算法是算法设计中的一种设计方法。

  • 期盼通过每个阶段的局部最优选择,从而达到全局的最优。

  • 结果不一定是最优。

leetcode题目

难度:简单

455. 分发饼干

122. 买卖股票的最佳时机 II

难度:中等

55. 跳跃游戏

/**
 * @param {number[]} nums
 * @return {boolean}
 */
var canJump = function(nums) {
  let ans = 0;
  for (let i = 0; i < nums.length; i++) {
    if (i > ans) {
      return false;
    }
    ans = Math.max(nums[i] + i, ans);
  }
  return true;
};

难度:困难

135. 分发糖果

/**
 * @param {number[]} ratings
 * @return {number}
 */
var candy = function(ratings) {
  let nums = new Array(ratings.length).fill(1);
  for (let i = 1; i < ratings.length; i++) {
    if (ratings[i] > ratings[i - 1]) {
      nums[i] = nums[i - 1] + 1;
    }
  }
  for (let i = ratings.length - 1; i > 0; i--) {
    if (ratings[i - 1] > ratings[i] && nums[i - 1] <= nums[i]) {
      nums[i - 1] = nums[i] + 1;
    }
  }
  return nums.reduce((a, b) => a + b, 0);
};

回溯算法

  • 回溯算法是算法设计中的一种设计方法。

  • 回溯算法是一种渐进式寻找并构建问题解决方式的策略。

  • 回溯算法会先从一个可能的动作开始解决问题,如果不行,就回溯并选择另一个动作,直到将问题解决。

  • 用递归模拟出所有情况。

  • 遇到包含重复元素的情况就回溯。

  • 收集所有到达递归终点的情况,并返回。

leetcode题目

46. 全排列

78. 子集