1 70爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1. 1 阶 + 1 阶
2. 2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1. 1 阶 + 1 阶 + 1 阶
2. 1 阶 + 2 阶
3. 2 阶 + 1 阶
function climbStairs (n) {
let dp = []
dp[0] = 0
dp[1] = 1
dp[2] = 2
for (let i = 3; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2]
}
console.log(dp[n])
return dp[n]
}
2 746使用最小花费爬楼梯
数组的每个索引作为一个阶梯,第 i个阶梯对应着一个非负数的体力花费值 cost[i](索引从0开始)。
每当你爬上一个阶梯你都要花费对应的体力花费值,然后你可以选择继续爬一个阶梯或者爬两个阶梯。
您需要找到达到楼层顶部的最低花费。在开始时,你可以选择从索引为 0 或 1 的元素作为初始阶梯。
示例 1:
输入: cost = [10, 15, 20]
输出: 15
解释: 最低花费是从cost[1]开始,然后走两步即可到阶梯顶,一共花费15。
示例 2:
输入: cost = [1, 100, 1, 1, 1, 100, 1, 1, 100, 1]
输出: 6
解释: 最低花费方式是从cost[0]开始,逐个经过那些1,跳过cost[3],一共花费6。
var minCostClimbingStairs = function (cost) {
cost.push(0) //注意末尾加一个0 使最后一步有机会迈两个台阶
let dp = []
dp[1] = cost[0]
dp[2] = Math.min(cost[0] + cost[1], cost[1])
for (let i = 3; i <= cost.length; i++) {
dp[i] = Math.min(dp[i - 1] + cost[i - 1], dp[i - 2] + cost[i - 1])
}
return dp[cost.length]
};
3 120 三角形最小路径和
给定一个三角形,找出自顶向下的最小路径和。每一步只能移动到下一行中相邻的结点上。
相邻的结点 在这里指的是 下标 与 上一层结点下标 相同或者等于 上一层结点下标 + 1 的两个结点。
例如,给定三角形:
[
[2],
[3,4],
[6,5,7],
[4,1,8,3]
]
自顶向下的最小路径和为 11(即,2 + 3 + 5 + 1 = 11)。
var minimumTotal = function (triangle) { //以最后一行作为base!!
// //base case最后一行
// let dp = []
// for (let i = 0; i < triangle[triangle.length - 1].length; i++) {
// dp[i] = triangle[triangle.length - 1][i]
// }
// for (let i = triangle.length - 2; i >= 0; i--) {
// for (let j = 0; j < triangle[i].length; j++) {
// dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j]
// }
// }
// return dp[0]
let dp = triangle[triangle.length - 1]
for (let i = triangle.length - 2; i >= 0; i--) {
for (let j = 0; j < triangle[i].length; j++) {
dp[j] = Math.min(dp[j], dp[j + 1]) + triangle[i][j]
}
}
return dp[0]
};
4 279 完全平方数
给定正整数 n,找到若干个完全平方数(比如 1, 4, 9, 16, ...)使得它们的和等于 n。你需要让组成和的完全平方数的个数最少。
示例 1:
输入: n = 12
输出: 3
解释: 12 = 4 + 4 + 4.
示例 2:
输入: n = 13
输出: 2
解释: 13 = 4 + 9.
var numSquares = function (n) {
//先找到所有小于等于n的完全平方数
let squares = []
for (let i = 1; i * i <= n; i++) {
squares.push(i * i)
}
let dp = []
dp[0] = 0
dp[1] = 1
for (let i = 2; i <= n; i++) {
dp[i] = i //最差的情况 就是n个1相加
for (let square of squares) {
if (i >= square) {
dp[i] = Math.min(dp[i], dp[i - square] + 1)
}
}
}
return dp[n]
};
5 62 不同路径
一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。
机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。
问总共有多少条不同的路径?
示例 1:
输入: m = 3, n = 2
输出: 3
解释:
从左上角开始,总共有 3 条路径可以到达右下角。
1. 向右 -> 向右 -> 向下
2. 向右 -> 向下 -> 向右
3. 向下 -> 向右 -> 向右
示例 2:
输入: m = 7, n = 3
输出: 28
var uniquePaths = function (m, n) {
//二维的动态规划 dp使二维的,表示到达这个点,有几种方法
//第一行和第一列都是1 先初始化一下
let dp = Array.from({ length: m }, () => Array.from({ length: n }, () => 0))
for (let i = 0; i < m; i++) {
dp[i][0] = 1
}
for (let i = 0; i < n; i++) {
dp[0][i] = 1
}
for (let i = 1; i < m; i++) {
for (let j = 1; j < n; j++) {
dp[i][j] = dp[i - 1][j] + dp[i][j - 1]
}
}
console.log(dp)
return dp[m - 1][n - 1]
};
6 322零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
示例 1:
输入: coins = [1, 2, 5], amount = 11
输出: 3
解释: 11 = 5 + 5 + 1
示例 2:
输入: coins = [2], amount = 3
输出: -1
var coinChange = function (coins, amount) {
let dp = new Array(amount + 1).fill(Infinity) //如果不能成功兑换就返回-1,也就是amount<coins[0]
dp[0] = 0
for (let i = 1; i <= amount; i++) {
for (let coin of coins) {
if (i >= coin) {
dp[i] = Math.min(dp[i], dp[i - coin] + 1)
}
}
}
return dp[amount] === Infinity ? -1 : dp[amount]
};
7 392判断子序列
给定字符串 s 和 t ,判断 s 是否为 t 的子序列。
你可以认为 s 和 t 中仅包含英文小写字母。字符串 t 可能会很长(长度 ~= 500,000),而 s 是个短字符串(长度 <=100)。
字符串的一个子序列是原始字符串删除一些(也可以不删除)字符而不改变剩余字符相对位置形成的新字符串。(例如,"ace"是"abcde"的一个子序列,而"aec"不是)。
示例 1:
s = "abc", t = "ahbgdc"
返回 true.
示例 2:
s = "axc", t = "ahbgdc"
返回 false.
//递归做法
var isSubsequence = function (s, t) {
if (s.length === 0) return true
let i = 0;
while (i < t.length) {
if (s[0] === t[i]) {
substr = s.substr(1)
resstr = t.substr(i + 1)
return isSubsequence(substr, resstr)
}
i++
}
return false
};
8 5最长回文子串
给定一个字符串 s,找到 s 中最长的回文子串。你可以假设 s 的最大长度为 1000。
示例 1:
输入: "babad"
输出: "bab"
注意: "aba" 也是一个有效答案。
示例 2:
输入: "cbbd"
输出: "bb
var longestPalindrome = function (s) {
//二维动态规划dp[i][j]表示i到j之间的子串是不是回文的,是就是true
let len = s.length
let res = ''
let dp = Array.from({ length: len }, () => Array.from({ length: len }))
for (let i = len - 1; i >= 0; i--) {
for (let j = i; j < len; j++) {
if (i === j) { //这个是保证中间夹一个的时候是回文的
dp[i][j] = true
} else if (s[i] === s[j] && j === i + 1) { //这个是对于中间夹一个回文
dp[i][j] = true
} else if (s[i] === s[j] && dp[i + 1][j - 1]) { //这个是对于中间夹2个及以上的情况
dp[i][j] = true
} else {
dp[i][j] = false
}
if (dp[i][j] && j - i + 1 > res.length) { //每次保存找到最大的那个子序列
res = s.slice(i, j + 1)
}
}
}
return res
};
9 1143 最长公共子序列
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
示例 1:
输入:text1 = "abcde", text2 = "ace"
输出:3
解释:最长公共子序列是 "ace",它的长度为 3。
示例 2:
输入:text1 = "abc", text2 = "abc"
输出:3
解释:最长公共子序列是 "abc",它的长度为 3。
示例 3:
输入:text1 = "abc", text2 = "def"
输出:0
解释:两个字符串没有公共子序列,返回 0。
var longestCommonSubsequence = function (text1, text2) {
let n = text1.length
let m = text2.length
let dp = Array.from({ length: n + 1 }, () => Array.from({ length: m + 1 }, () => 0))
for (let i = 1; i < n + 1; i++) {
for (let j = 1; j < m + 1; 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])
}
}
}
console.log(dp)
return dp[n][m]
};
10 300最长上升子序列
给定一个无序的整数数组,找到其中最长上升子序列的长度。
示例:
输入: [10,9,2,5,3,7,101,18]
输出: 4
解释: 最长的上升子序列是 [2,3,7,101],它的长度是 4。
var lengthOfLIS = function (nums) { //dp[i]的意思是在下标为i的位置,最长子序列有多长
let dp = []
dp[0] = 1
let len = nums.length
for (let i = 1; i <= len; i++) {
dp[i] = 1
for (let j = i; j >= 0; j--) {
if (nums[i] > nums[j]) { //找到比自己小的元素,取最大的那个 拼接上
dp[i] = Math.max(dp[j] + 1, dp[i])
}
}
}
return Math.max(...dp)
};
11 121买卖股票的最佳时机
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
如果你最多只允许完成一笔交易(即买入和卖出一支股票一次),设计一个算法来计算你所能获取的最大利润。
注意:你不能在买入股票前卖出股票。
示例 1:
输入: [7,1,5,3,6,4]
输出: 5
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
var maxProfit = function (prices) {
let minprice = prices[0] //维护一个最低价
let maxprofit = 0
for (let i = 1; i < prices.length; i++) {
minprice = Math.min(prices[i], minprice)
maxprofit = Math.max(price[i] - minprice, maxprofit) //每次取一个最高利润
}
return maxprofit
}
12 122买股票的最佳时机2
给定一个数组,它的第 i 个元素是一支给定股票第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你可以尽可能地完成更多的交易(多次买卖一支股票)。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入: [7,1,5,3,6,4]
输出: 7
解释: 在第 2 天(股票价格 = 1)的时候买入,在第 3 天(股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
随后,在第 4 天(股票价格 = 3)的时候买入,在第 5 天(股票价格 = 6)的时候卖出, 这笔交易所能获得利润 = 6-3 = 3 。
示例 2:
输入: [1,2,3,4,5]
输出: 4
解释: 在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入: [7,6,4,3,1]
输出: 0
解释: 在这种情况下, 没有交易完成, 所以最大利润为 0。
var maxProfit = function (prices) {
let dp = Array.from({ length: prices.length }, () => Array.from({ length: 2 }))
console.log(dp)
dp[0][0] = 0
dp[0][1] = -prices[0]
for (let i = 1; i < prices.length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]) //今天不持有的两种情况:1昨天持有今天卖了2昨天不持有
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]) //今天持有的两种情况:1昨天不持有今天买了2昨天持有
}
console.log(dp)
return dp[prices.length - 1][0]
};