leecode更新

This commit is contained in:
markilue 2022-12-12 13:18:21 +08:00
parent 620b260e4a
commit ec0018c737
2 changed files with 305 additions and 0 deletions

View File

@ -0,0 +1,209 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.dynamic
* @Author: dingjiawen
* @CreateTime: 2022-12-12 10:02
* @Description:
* TODO 力扣121题 买卖股票的最佳时期:
* 给定一个数组 prices 它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格
* 你只能选择 某一天 买入这只股票并选择在 未来的某一个不同的日子 卖出该股票设计一个算法来计算你所能获取的最大利润
* 返回你可以从这笔交易中获取的最大利润如果你不能获取任何利润返回 0
* @Version: 1.0
*/
public class T21_MaxProfit {
@Test
public void test(){
int[] price= {7, 1, 5, 3, 6, 4};
System.out.println(maxProfit1(price));
}
@Test
public void test1(){
int[] price= {7,6,4,3,1};
System.out.println(maxProfit1(price));
}
/**
* 思路:第一反应还是一个背包问题即在第i天所能获得的最大但似乎是一个O(n^2)复杂度
* TODO 动态规划五部曲
* (1)dp定义:dp[i][j]表示在第i天买入在第j天的最大利润
* (2)dp状态转移方程:dp[i][j]=max(dp[i][j-1],prices[j]-prices[i],dp[i-1][j])
* (3)dp初始化:dp[i][j<=i]=0
* (4)dp遍历顺序:
* (5)dp举例推导:
* 到第198个案例后超出内存限制
* @param prices
* @return
*/
public int maxProfit(int[] prices) {
if(prices.length==1){
return 0;
}
int[][] dp = new int[prices.length][prices.length];
for (int i = 1; i < prices.length; i++) {
dp[0][i]=Math.max(dp[0][i-1],prices[i]-prices[0]);
}
for (int i = 1; i < prices.length; i++) {
for (int j = i+1; j < prices.length; j++) {
dp[i][j]=Math.max(Math.max(dp[i][j-1],prices[j]-prices[i]),dp[i-1][j]);
}
}
return dp[prices.length-2][prices.length-1];
}
/**
* 思路:尝试一遍遍历思路遇见负数就将现在的值置为最小值,本质上是一种贪心
* 速度击败55.26%内存击败71.51% 2ms
* @param prices
* @return
*/
public int maxProfit1(int[] prices) {
if(prices.length==1){
return 0;
}
int max=0;
int min=prices[0];
for (int i = 1; i < prices.length; i++) {
if(prices[i]<min) {
min = prices[i];
continue;
}
max=Math.max(prices[i]-min,max);
}
return max;
}
/**
* 官方贪心法与本人的贪心类似比本人快第一个if,并将Math.max替换为下面的else if
* 速度击败100%内存击败30.17%
* @param prices
* @return
*/
public int maxProfit2(int prices[]) {
int minprice = Integer.MAX_VALUE;
int maxprofit = 0;
for (int i = 0; i < prices.length; i++) {
if (prices[i] < minprice) {
minprice = prices[i];
} else if (prices[i] - minprice > maxprofit) {
maxprofit = prices[i] - minprice;
}
}
return maxprofit;
}
/**
* 评论区从右往左动态规划法比较巧妙
* 巧妙在于当天(i)的买入最大利润=i+1天以后的最大值-当天的价格
* 而i+1天以后的最大值=i+1天的利润+(i+1)时的股票价格
* 所以当天利润=i+1天的利润+(i+1)时的股票价格-当天的价格
* 速度击败30.8%内存击败98%
* @param prices
* @return
*/
public int maxProfit3(int[] prices) {
int len=prices.length;
int[] dp=new int[len];//每天买入股票对应能获取的最大利润
int max=0;//最大利润
dp[len-1]=0;//最后一天买入的话利润为0
for(int i=len-2;i>=0;i--){
//因为利润最大的话当然是选择价格最高的时候卖出
//所以dp[i+1]=第i+1天之后最高价格- prices[i+1];
//如果dp[i+1]=0,那么第i天之后最高价格只可能是prices[i+1]
//如果dp[i+1]>0就是dp[i+1]+prices[i+1];
//所以第i天买入能获得的最大利润就是profit
//如果第i天价格比之后都高的话那么profit<0
int profit=dp[i+1]+prices[i+1]-prices[i];
dp[i]=profit>0 ? profit : 0;
max=profit>max ? profit : max;
}
return max;
}
/**
* 代码随想录动态规划法(版本一)
* TODO 动态规划五部曲:
* (1)dp定义:dp[i][0]表示第i天持有股票的最多现金;dp[i][1]表示第i天不持有股票的最多现金
* (2)dp状态转移方程:
* 1.dp[i][0] 注意题目规定只能买入一次所以有下面的推导
* dp[i][0]表示当天持有所以可以从两个情况获得:昨天就持有了或者今天才买入
* dp[i][0]=max(dp[i-1][0],-price[i])
* 2.dp[i][1]
* dp[i][1]表示当天不持有所以也可以从两种情况获得:昨天不持有;或者今天才卖出
* dp[i][1]=max(dp[i-1][1],price[i]+dp[i-1][0])
* (3)dp初始化:dp[0][0]=-price[0];dp[0][1]=0
* (4)dp遍历顺序:从状态转移方程可以看出从前往后
* (5)dp距离推导输入[7,1,5,3,6,4]为例dp数组状态如下
* [0,1]
* i=0 -7 0
* i=1 -1 0
* i=2 -1 4
* i=3 -1 4
* i=4 -1 5
* i=5 -1 5
* 速度击败6.7%内存击败89.24%
* @param prices
* @return
*/
public int maxProfit4(int[] prices) {
if (prices == null || prices.length == 0) return 0;
int length = prices.length;
// dp[i][0]代表第i天持有股票的最大收益
// dp[i][1]代表第i天不持有股票的最大收益
int[][] dp = new int[length][2];
int result = 0;
dp[0][0] = -prices[0];
dp[0][1] = 0;
for (int i = 1; i < length; i++) {
dp[i][0] = Math.max(dp[i - 1][0], -prices[i]);
dp[i][1] = Math.max(dp[i - 1][0] + prices[i], dp[i - 1][1]);
}
return dp[length - 1][1];
}
/**
* 代码随想录动态规划法(版本二):滚动数组记录前一天的即可
* 速度击败55.26%内存击败16.18%
* @param prices
* @return
*/
public int maxProfit5(int[] prices) {
int[] dp = new int[2];
// 记录一次交易一次交易有买入卖出两种状态
// 0代表持有1代表卖出
dp[0] = -prices[0];
dp[1] = 0;
// 可以参考斐波那契问题的优化方式
// 我们从 i=1 开始遍历数组一共有 prices.length
// 所以是 i<=prices.length
for (int i = 1; i <= prices.length; i++) {
// 前一天持有或当天买入
dp[0] = Math.max(dp[0], -prices[i - 1]);
// 如果 dp[0] 被更新那么 dp[1] 肯定会被更新为正数的 dp[1]
// 而不是 dp[0]+prices[i-1]==0 的0
// 所以这里使用会改变的dp[0]也是可以的
// 当然 dp[1] 初始值为 0 被更新成 0 也没影响
// 前一天卖出或当天卖出, 当天要卖出得前一天持有才行
dp[1] = Math.max(dp[1], dp[0] + prices[i - 1]);
}
return dp[1];
}
}

View File

@ -0,0 +1,96 @@
package com.markilue.leecode.dynamic;
import org.junit.Test;
/**
* @BelongsProject: Leecode
* @BelongsPackage: com.markilue.leecode.dynamic
* @Author: dingjiawen
* @CreateTime: 2022-12-12 12:23
* @Description:
* TODO 力扣122题 买卖股票的最佳时机 II:
* 给你一个整数数组 prices 其中 prices[i] 表示某支股票第 i 天的价格
* 在每一天你可以决定是否购买和/或出售股票你在任何时候 最多 只能持有 一股 股票你也可以先购买然后在 同一天 出售
* 返回 你能获得的 最大 利润
* @Version: 1.0
*/
public class T22_MaxProfit {
@Test
public void test(){
int[] prices = {7, 1, 5, 3, 6, 4};
System.out.println(maxProfit(prices));
}
@Test
public void test1(){
int[] prices = {1,2,3,4,5};
System.out.println(maxProfit(prices));
}
/**
* 思路这题实际上在贪心的时候遇到过这里使用动态规划法
* TODO 动态规划法
* (1)dp定义:dp[i][0]表示当天不留股票的最大收益dp[i][1]表示当天留股票的最大收益
* (2)dp状态转移方程:
* 1.dp[i][0]=昨天不留+今天也不留 昨天留了今天卖了
* dp[i][0]=max(dp[i-1][0],dp[i-1][1]+price[i])
* 2.dp[i][1]=昨天留了今天没买昨天没留今天买了
* dp[i][1]=max(dp[i-1][1],dp[i-1][0]-price[i]))
* (3)dp初始化:dp[0][0]=0;dp[0][1]=-price[0]
* (4)dp遍历顺序:从前往后
* (5)dp举例推导:以prices = [7,1,5,3,6,4]为例
* [0 1]
* i=0 0 -7
* i=1 0 -1
* i=2 4 -1
* i=3 4 1
* i=4 7 1
* i=5 7 3
* 速度击败24.78%内存击败28.24% 3ms
*/
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
dp[0][0]=0;
dp[0][1]=-prices[0];
for (int i = 1; i < prices.length; 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[prices.length-1][0];
}
/**
* 对上述思路进行简化使用滑动数组记录上次
* 速度击败82.36%内存击败66.41%
*/
public int maxProfit1(int[] prices) {
int dp0=0;
int dp1=-prices[0];
for (int i = 1; i < prices.length; i++) {
dp0=Math.max(dp0,dp1+prices[i]);
dp1=Math.max(dp1,dp0-prices[i]);
}
return dp0;
}
/**
* 官方贪心法本质上就是利润累加
* @param prices
* @return
*/
public int maxProfit2(int[] prices) {
int ans = 0;
int n = prices.length;
for (int i = 1; i < n; ++i) {
ans += Math.max(0, prices[i] - prices[i - 1]);
}
return ans;
}
}