剑指动态规划:算法优化,化繁为简!
2023-12-28 15:30:19
入门题
斐波那契数列
斐波那契数列的定义是:F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2)(n >= 2)。请编写一个函数,给定一个整数 n,返回第 n 个斐波那契数。
官方题目: https://leetcode.cn/problems/fibonacci-number/
题解:
使用动态规划,建立一个 dp 数组,存储前面 n 个斐波那契数。当 n 小于等于 1 时,直接返回 n。否则,返回 dp[n-1] + dp[n-2]。
const fib = (n) => {
if (n <= 1) {
return n;
}
const dp = [0, 1];
for (let i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
};
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。每次你可以爬 1 或 2 阶。有多少种不同的方法可以爬到楼顶呢?
官方题目: https://leetcode.cn/problems/climbing-stairs/
题解:
定义 dp[i] 为爬到第 i 阶楼梯的方法数。那么,爬到第 i 阶楼梯的方法数要么是从第 i-1 阶爬上来,要么是从第 i-2 阶爬上来。因此,dp[i] = dp[i-1] + dp[i-2]。
const climbStairs = (n) => {
if (n <= 2) {
return n;
}
const dp = [0, 1, 2];
for (let i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
};
硬币找零
给你一个整数数组 coins,表示不同面额的硬币;以及一个整数 amount,表示总金额。请计算有多少种组合方式可以凑成总金额。
官方题目: https://leetcode.cn/problems/coin-change-2/
题解:
定义 dp[i][j] 为使用前 i 个硬币凑成金额 j 的组合数。那么,如果第 i 个硬币的面额为 coins[i-1],则凑成金额 j 的组合数要么是使用前 i-1 个硬币凑成金额 j,要么是使用前 i 个硬币凑成金额 j-coins[i-1]。因此,dp[i][j] = dp[i-1][j] + dp[i][j-coins[i-1]]。
const change = (coins, amount) => {
const dp = new Array(coins.length + 1).fill(0).map(() => new Array(amount + 1).fill(0));
dp[0][0] = 1;
for (let i = 1; i <= coins.length; i++) {
for (let j = 0; j <= amount; j++) {
dp[i][j] = dp[i - 1][j];
if (j - coins[i - 1] >= 0) {
dp[i][j] += dp[i][j - coins[i - 1]];
}
}
}
return dp[coins.length][amount];
};
进阶题
最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
官方题目: https://leetcode.cn/problems/longest-common-subsequence/
题解:
定义 dp[i][j] 为字符串 text1 的前 i 个字符和字符串 text2 的前 j 个字符的最长公共子序列的长度。那么,如果 text1 的第 i 个字符与 text2 的第 j 个字符相等,则 dp[i][j] = dp[i-1][j-1] + 1。否则,dp[i][j] = max(dp[i-1][j], dp[i][j-1])。
const longestCommonSubsequence = (text1, text2) => {
const m = text1.length;
const n = text2.length;
const dp = new Array(m + 1).fill(0).map(() => new Array(n + 1).fill(0));
for (let i = 1; i <= m; i++) {
for (let j = 1; j <= n; j++) {
if (text1[i - 1] === text2[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
}
}
}
return dp[m][n];
};
最大子数组和
给定一个整数数组 nums,找到一个连续子数组,其和最大。返回最大的和。
官方题目: https://leetcode.cn/problems/maximum-subarray/
题解:
定义 dp[i] 为以第 i 个元素结尾的连续子数组的最大和。那么,dp[i] 要么是 dp[i-1] 加上第 i 个元素,要么是第 i 个元素。因此,dp[i] = max(dp[i-1] + nums[i], nums[i])。
const maxSubArray = (nums) => {
const dp = new Array(nums.length).fill(0);
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);
};
总结
通过以上题目的讲解,相信你已经对动态规划有了更深入的理解。动态规划是一种强大的算法设计技术,可以有效解决许多原本复杂的算法问题。在 leetcode 刷题的过程中,掌握动态规划的精髓至关重要。希望本文能为你的动态规划学习之旅增添一份助力。