文章目录
打家劫舍1
你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。
给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。
示例 1:
输入:[1,2,3,1]
输出:4
解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 2:
输入:[2,7,9,3,1]
输出:12
解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。
偷窃到的最高金额 = 2 + 9 + 1 = 12 。
动态规划
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if (len == 0)
return 0;
if (len == 1)
return nums[0];
int[] dp = new int[len];
dp[0] = nums[0];
dp[1] = Math.max(nums[0], nums[1]);
for (int i = 2; i < len; i++)
dp[i] = Math.max(dp[i - 2] + nums[i], dp[i - 1]);
return dp[len - 1];
}
}
动态规划+滚动数组
class Solution {
public int rob(int[] nums) {
int prev = 0, curr = 0, temp = 0;
for (int num : nums) {
temp = Math.max(curr, prev + num);
prev = curr;
curr = temp;
}
return curr;
}
}
打家劫舍2
你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。
给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。
示例 1:
输入:nums = [2,3,2]
输出:3
解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。
示例 2:
输入:nums = [1,2,3,1]
输出:4
解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。
偷窃到的最高金额 = 1 + 3 = 4 。
示例 3:
输入:nums = [0]
输出:0
动态规划
class Solution {
public int rob(int[] nums) {
int len = nums.length;
if (len == 0)
return 0;
if (len == 1)
return nums[0];
return Math.max(rob(nums, 0, len -1), rob(nums, 1, len));
}
public int rob(int[] nums, int start, int end) {
int prev = 0, curr = 0, temp = 0;
for (int i = start; i < end; i++) {
temp = Math.max(curr, prev + nums[i]);
prev = curr;
curr = temp;
}
return curr;
}
}
买卖股票的最佳时机1
给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。
你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算法来计算你所能获取的最大利润。
返回你可以从这笔交易中获取的最大利润。如果你不能获取任何利润,返回 0 。
示例 1:
输入:[7,1,5,3,6,4]
输出:5
解释:在第 2 天(股票价格 = 1)的时候买入,在第 5 天(股票价格 = 6)的时候卖出,最大利润 = 6-1 = 5 。
注意利润不能是 7-1 = 6, 因为卖出价格需要大于买入价格;同时,你不能在买入前卖出股票。
示例 2:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这种情况下, 没有交易完成, 所以最大利润为 0。
单调栈
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int max = 0;
Deque<Integer> stack = new LinkedList<>();
for (int i = 0; i <= n; i++) {
int price = i == n ? -1 : prices[i];
while (!stack.isEmpty() && stack.peekLast() > price) {
max = Math.max(max, stack.peekLast() - stack.peekFirst());
stack.pollLast();
}
stack.offerLast(price);
}
return max;
}
}
动态规划
dp[i][j]:下标为 i 这一天结束的时候,手上持股状态为 j 时,我们持有的现金数。
- j = 0,表示当前不持股;
- j = 1,表示当前持股。
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < n; i++) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], -prices[i]);
}
return dp[n - 1][0];
}
}
动态规划+滚动数组
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] dp = new int[2];
dp[0] = 0;
dp[1] = -prices[0];
for (int i = 1; i < n; i++) {
dp[0] = Math.max(dp[0], dp[1] + prices[i]);
dp[1] = Math.max(dp[1], -prices[i]);
}
return dp[0];
}
}
贪心
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int min_price = Integer.MAX_VALUE;
int max_profit = 0;
for (int i = 0; i < n; i++) {
min_price = Math.min(min_price, prices[i]);
max_profit = Math.max(max_profit, prices[i] - min_price);
}
return max_profit;
}
}
买卖股票的最佳时机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。
贪心
class Solution {
public int maxProfit(int[] prices) {
int max_profit = 0;
int n = prices.length;
for (int i = 1; i < prices.length; i++) {
max_profit += Math.max(0, prices[i] - prices[i - 1]);
}
return max_profit;
}
}
动态规划
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[][] dp = new int[n][2];
dp[0][0] = 0;
dp[0][1] = -prices[0];
for (int i = 1; i < n; ++i) {
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
return dp[n - 1][0];
}
}
买卖股票的最佳时机3
给定一个数组,它的第 i 个元素是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 两笔 交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:prices = [3,3,5,0,0,3,1,4]
输出:6
解释:在第 4 天(股票价格 = 0)的时候买入,在第 6 天(股票价格 = 3)的时候卖出,这笔交易所能获得利润 = 3-0 = 3 。
随后,在第 7 天(股票价格 = 1)的时候买入,在第 8 天 (股票价格 = 4)的时候卖出,这笔交易所能获得利润 = 4-1 = 3 。
示例 2:
输入:prices = [1,2,3,4,5]
输出:4
解释:在第 1 天(股票价格 = 1)的时候买入,在第 5 天 (股票价格 = 5)的时候卖出, 这笔交易所能获得利润 = 5-1 = 4 。
注意你不能在第 1 天和第 2 天接连购买股票,之后再将它们卖出。
因为这样属于同时参与了多笔交易,你必须在再次购买前出售掉之前的股票。
示例 3:
输入:prices = [7,6,4,3,1]
输出:0
解释:在这个情况下, 没有交易完成, 所以最大利润为 0。
示例 4:
输入:prices = [1]
输出:0
贪心
先从左到右找出每天的最大利润,再从右到左找出 当天买入最大利润 和 前一天最大利润 之和。
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int[] profits = new int[n];
profits[0] = 0;
int min_price = prices[0];
for (int i = 1; i < n; i++) {
min_price = Math.min(min_price, prices[i]);
profits[i] = prices[i] - min_price;
}
int max_profit_sum = profits[n - 1];
int max_price = prices[n - 1];
int cur_profit = 0;
for (int i = n - 1; i > 1; i--) {
max_price = Math.max(max_price, prices[i]);
cur_profit = Math.max(cur_profit, max_price - prices[i]);
max_profit_sum = Math.max(max_profit_sum, cur_profit + profits[i - 1]);
}
return max_profit_sum;
}
}
动态规划
由于我们最多可以完成两笔交易,因此在任意一天结束之后,我们会处于以下五个状态中的一种:
- 未进行过任何操作;
- 只进行过一次买操作;
- 进行了一次买操作和一次卖操作,即完成了一笔交易;
- 在完成了一笔交易的前提下,进行了第二次买操作;
- 完成了全部两笔交易。
由于第一个状态的利润显然为 0,因此我们可以不用将其记录。对于剩下的四个状态,我们分别将它们的最大利润记为 buy 1 \textit{buy}_1 buy1, sell 1 \textit{sell}_1 sell1, buy 2 \textit{buy}_2 buy2, sell 2 \textit{sell}_2 sell2。
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
int buy1 = -prices[0], sell1 = 0;
int buy2 = -prices[0], sell2 = 0;
for (int i = 1; i < n; ++i) {
buy1 = Math.max(buy1, -prices[i]);
sell1 = Math.max(sell1, buy1 + prices[i]);
buy2 = Math.max(buy2, sell1 - prices[i]);
sell2 = Math.max(sell2, buy2 + prices[i]);
}
return sell2;
}
}
买卖股票的最佳时机4
给定一个整数数组 prices ,它的第 i 个元素 prices[i] 是一支给定的股票在第 i 天的价格。
设计一个算法来计算你所能获取的最大利润。你最多可以完成 k 笔交易。
注意:你不能同时参与多笔交易(你必须在再次购买前出售掉之前的股票)。
示例 1:
输入:k = 2, prices = [2,4,1]
输出:2
解释:在第 1 天 (股票价格 = 2) 的时候买入,在第 2 天 (股票价格 = 4) 的时候卖出,这笔交易所能获得利润 = 4-2 = 2 。
示例 2:
输入:k = 2, prices = [3,2,6,5,0,3]
输出:7
解释:在第 2 天 (股票价格 = 2) 的时候买入,在第 3 天 (股票价格 = 6) 的时候卖出, 这笔交易所能获得利润 = 6-2 = 4 。
随后,在第 5 天 (股票价格 = 0) 的时候买入,在第 6 天 (股票价格 = 3) 的时候卖出, 这笔交易所能获得利润 = 3-0 = 3 。
动态规划
class Solution {
public int maxProfit(int k, int[] prices) {
int len = prices.length;
if (len == 0)
return 0;
k = Math.min(k, len / 2);
int[] buy = new int[k + 1];
int[] sell = new int[k + 1];
sell[0] = 0;
for (int j = 1; j <= k; j++)
buy[j] = -prices[0];
for (int i = 1; i < len; ++i) {
for (int j = 1; j <= k; j++) {
buy[j] = Math.max(buy[j], sell[j - 1] - prices[i]);
sell[j] = Math.max(sell[j], buy[j] + prices[i]);
}
}
return sell[k];
}
}
爬楼梯
假设你正在爬楼梯。需要 n 阶你才能到达楼顶。
每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢?
注意:给定 n 是一个正整数。
示例 1:
输入: 2
输出: 2
解释: 有两种方法可以爬到楼顶。
1 阶 + 1 阶
2 阶
示例 2:
输入: 3
输出: 3
解释: 有三种方法可以爬到楼顶。
1 阶 + 1 阶 + 1 阶
1 阶 + 2 阶
2 阶 + 1 阶
动态规划
我们用 f(x) 表示爬到第 x 级台阶的方案数,考虑最后一步可能跨了一级台阶,也可能跨了两级台阶,所以我们可以得到 f(x) = f(x - 1) + f(x - 2)。
class Solution {
public int climbStairs(int n) {
int fx_2 = 0, fx_1 = 0, fx = 1;
for (int i = 0; i < n; ++i) {
fx_2 = fx_1;
fx_1 = fx;
fx = fx_2 + fx_1;
}
return fx;
}
}
class Solution {
public int climbStairs(int n) {
if (n < 3)
return n;
// dp[x][y] 爬到 x 级,且最后一步为 y 级的情况有多少种
int[][] dp = new int[n + 1][3];
dp[1][1] = dp[2][1] = dp[2][2] = 1;
for (int i = 3; i <= n; i++) {
dp[i][1] = dp[i - 1][1] + dp[i - 1][2];
dp[i][2] = dp[i - 2][1] + dp[i - 2][2];
}
return dp[n][1] + dp[n][2];
}
}
零钱兑换
给定不同面额的硬币 coins 和一个总金额 amount。编写一个函数来计算可以凑成总金额所需的最少的硬币个数。如果没有任何一种硬币组合能组成总金额,返回 -1。
你可以认为每种硬币的数量是无限的。
示例 1:
输入:coins = [1, 2, 5], amount = 11
输出:3
解释:11 = 5 + 5 + 1
示例 2:
输入:coins = [2], amount = 3
输出:-1
示例 3:
输入:coins = [1], amount = 0
输出:0
示例 4:
输入:coins = [1], amount = 1
输出:1
示例 5:
输入:coins = [1], amount = 2
输出:2
记忆化DFS
class Solution {
private int[] dp;
public int coinChange(int[] coins, int amount) {
dp = new int[amount + 1];
return dfs(coins, amount);
}
private int dfs(int[] coins, int amount) {
if (amount <= 0)
return amount == 0 ? 0 : -1;
if (dp[amount] != 0)
return dp[amount];
int count = Integer.MAX_VALUE;
for (int i = coins.length - 1; i >= 0; i--) {
int min = dfs(coins, amount - coins[i]);
if (min != -1 && min < count)
count = min + 1;
}
return dp[amount] = (count == Integer.MAX_VALUE ? -1 : count);
}
}
动态规划
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount + 1];
Arrays.fill(dp, amount + 1);
dp[0] = 0;
for (int coin : coins) {
for (int i = coin; i <= amount; i++)
dp[i] = Math.min(dp[i], dp[i - coin] + 1);
}
return dp[amount] > amount ? -1 : dp[amount];
}
}