本文记录动态规划算法设计思想
动态规划
-
动态规划是算法设计中的一种设计方法。
-
它将一个问题分解为相互重叠的子问题,通过反复求解子问题,来解决原来的问题。
动态规划 vs 分而治之
动态规划是分解为相互重叠的子问题,如斐波那契数列,F(n) = F(n-1) + F(n-2)。
分而治之分解出来的子问题是独立的子问题。如翻转二叉树。
leetcode题目
/**
* @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;
}
/**
* @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;
}
/**
* @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)
/**
* @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题目
难度:简单
难度:中等
/**
* @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;
};
难度:困难
/**
* @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);
};
回溯算法
-
回溯算法是算法设计中的一种设计方法。
-
回溯算法是一种渐进式寻找并构建问题解决方式的策略。
-
回溯算法会先从一个可能的动作开始解决问题,如果不行,就回溯并选择另一个动作,直到将问题解决。
-
用递归模拟出所有情况。
-
遇到包含重复元素的情况就回溯。
-
收集所有到达递归终点的情况,并返回。