文章目錄
- [121\. 買賣一次:買賣股票的最佳時機](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock/)
- [122\. 買賣次數不限:買賣股票的最佳時機 II](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-ii/)
- [123\. 最多2筆法:買賣股票的最佳時機 III](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iii/)
- [188\. 最多k筆:買賣股票的最佳時機 IV](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-iv/)
- [309\. 冷凍期:最佳買賣股票時機含冷凍期](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-cooldown/)
- [714\. 手續費:買賣股票的最佳時機含手續費](https://leetcode-cn.com/problems/best-time-to-buy-and-sell-stock-with-transaction-fee/)
- 參考link(這是真大佬)
121. 買賣一次:買賣股票的最佳時機
Difficulty: 簡單
給定一個數組,它的第 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。
Solution
轉化爲最大子序和問題,詳解戳開鏈接
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0) return 0;
int[] fluc=new int[prices.length-1];
for(int i=1;i!=prices.length;i++) {
fluc[i-1]=prices[i]-prices[i-1];
}
int curMaxWithRight=0;
int res=0;
for(int j=0;j!=fluc.length;j++) {
curMaxWithRight=Math.max(curMaxWithRight+fluc[j],fluc[j]);
res=Math.max(res, curMaxWithRight);
}
return res;
}
}
存儲波底和最大收益 O(n)
我們需要找到最小的谷之後的最大的峯。
我們可以維持兩個變量——minprice 和 maxprofit,它們分別對應迄今爲止所得到的最小的谷值和最大的利潤(賣出價格與最低價格之間的最大差值)。
class Solution {
public int maxProfit(int[] prices) {
int minPrice=Integer.MAX_VALUE;//波底
int maxProfit=0;//最大獲利
for(int i=0;i!=prices.length;i++) {
minPrice=Math.min(minPrice, prices[i]);
maxProfit=Math.max(maxProfit, prices[i]-minPrice);
}
return maxProfit;
}
}
自底向上動態優化問題
O(n)空間複雜度
class Solution {
public int maxProfit(int[] prices) {
//爲空賺個屁
if(prices==null||prices.length==0) return 0;
//動態規劃 狀態轉移
int[][]dp=new int[prices.length][2];
//第一天不買 獲利0元 ; 第一天手裏已經買了 獲利第一天股票的負數
dp[0][0]=0;dp[0][1]=-prices[0];
for(int i=1;i!=prices.length;i++) {
//前一天也是沒有買 or前一天買了但今天賣出去了
dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
//前一天時就已經買了or前一天還沒買,今天才買的(本金爲0)
dp[i][1]=Math.max(dp[i-1][1],0-prices[i]);
}
return dp[prices.length-1][0];
}
}
O(1)空間複雜度
class Solution {
public int maxProfit(int[] prices) {
//爲空賺個屁
if(prices==null||prices.length==0) return 0;
//第一天不買 獲利0元 ; 第一天手裏已經買了 獲利第一天股票的負數
int yesterdayYes=-prices[0];//昨天買了
int yesterdayNo=0;//昨天沒有買
for(int i=1;i!=prices.length;i++) {
//前一天也是沒有買 or前一天買了但今天賣出去了
yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
//前一天時就已經買了or前一天還沒買,今天才買的(本金爲0)
yesterdayYes=Math.max(yesterdayYes,0-prices[i]);
}
//最終返回的是最後一天,手中沒有持有股票的情況
return yesterdayNo;
}
}
122. 買賣次數不限:買賣股票的最佳時機 II
Difficulty: 簡單
給定一個數組,它的第 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。
Solution
動態優化自底向上
O(n)空間複雜度
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0) return 0;
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]);
//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
dp[i][1]=Math.max(dp[i-1][1],dp[i-1][0]-prices[i]);
}
return dp[prices.length-1][0];
}
}
O(1)空間複雜度
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0) return 0;
int yesterdayNo=0,yesterdayYes=-prices[0];
for(int i=1;i!=prices.length;i++) {
//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
yesterdayYes=Math.max(yesterdayYes,yesterdayNo-prices[i]);
}
return yesterdayNo;
}
}
123. 最多2筆法:買賣股票的最佳時機 III
Difficulty: 困難
給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個算法來計算你所能獲取的最大利潤。你最多可以完成 _兩筆 _交易。
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例 1:
輸入: [3,3,5,0,0,3,1,4]
輸出: 6
解釋: 在第 4 天(股票價格 = 0)的時候買入,在第 6 天(股票價格 = 3)的時候賣出,這筆交易所能獲得利潤 = 3-0 = 3 。
隨後,在第 7 天(股票價格 = 1)的時候買入,在第 8 天 (股票價格 = 4)的時候賣出,這筆交易所能獲得利潤 = 4-1 = 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。
Solution
動態優化 解決 k次買賣問題
空間 O(n*k)
class Solution {
public int maxProfit(int[] prices) {
if(prices==null||prices.length==0) return 0;
int K=2; //本題爲k次
//K+1是爲了後面k爲0時準備的(int數組初始化默認爲0)
int[][][]dp=new int[prices.length][K+1][2];
// 初始化第一天
for(int k=0;k!=K+1;k++) {
dp[0][k][0]=0;
dp[0][k][1]=-prices[0];
}
for(int i=1;i!=prices.length;i++) {
//k 表示 此時爲第k次買|賣
for(int k=K;k!=0;k--) {
//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
dp[i][k][0]=Math.max(dp[i-1][k][0],dp[i-1][k][1]+prices[i]);
//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
dp[i][k][1]=Math.max(dp[i-1][k][1],dp[i-1][k-1][0]-prices[i]);
}
}
return dp[prices.length-1][K][0];
}
}
188. 最多k筆:買賣股票的最佳時機 IV
Difficulty: 困難
給定一個數組,它的第 i 個元素是一支給定的股票在第 i 天的價格。
設計一個算法來計算你所能獲取的最大利潤。你最多可以完成 k 筆交易。
注意: 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
示例 1:
輸入: [2,4,1], k = 2
輸出: 2
解釋: 在第 1 天 (股票價格 = 2) 的時候買入,在第 2 天 (股票價格 = 4) 的時候賣出,這筆交易所能獲得利潤 = 4-2 = 2 。
示例 2:
輸入: [3,2,6,5,0,3], k = 2
輸出: 7
解釋: 在第 2 天 (股票價格 = 2) 的時候買入,在第 3 天 (股票價格 = 6) 的時候賣出, 這筆交易所能獲得利潤 = 6-2 = 4 。
隨後,在第 5 天 (股票價格 = 0) 的時候買入,在第 6 天 (股票價格 = 3) 的時候賣出, 這筆交易所能獲得利潤 = 3-0 = 3 。
Solution
Language: Java
class Solution {
public int maxProfit(int k,int[] prices) {
if(prices==null||prices.length==0) return 0;
//如果k大於prices的一半了,不可能滿足 "必須在再次購買前出售掉之前的股票" 這個要求,相當於不限次數買賣
if(k>prices.length/2) return maxProfitInf(prices);
//K+1是爲了後面k爲0時準備的(int數組初始化默認爲0)
int[][][]dp=new int[prices.length][k+1][2];
// 初始化第一天
for(int time=0;time!=k+1;time++) {
dp[0][time][0]=0;
dp[0][time][1]=-prices[0];
}
for(int i=1;i!=prices.length;i++) {
//k 表示 此時爲第k次買|賣
for(int time=k;time!=0;time--) {
//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
dp[i][time][0]=Math.max(dp[i-1][time][0],dp[i-1][time][1]+prices[i]);
//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
dp[i][time][1]=Math.max(dp[i-1][time][1],dp[i-1][time-1][0]-prices[i]);
}
}
return dp[prices.length-1][k][0];
}
private int maxProfitInf(int[] prices) {
int yesterdayNo=0,yesterdayYes=-prices[0];
for(int i=1;i!=prices.length;i++) {
//前一天也是手裏沒股票 or前一天手裏有股票但今天賣出去了
yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
//前一天時就手裏有股票or前一天手裏沒股票,今天才買的(本金爲上次累積的)
yesterdayYes=Math.max(yesterdayYes,yesterdayNo-prices[i]);
}
return yesterdayNo;
}
}
309. 冷凍期:最佳買賣股票時機含冷凍期
Difficulty: 中等
給定一個整數數組,其中第_ i_ 個元素代表了第 i 天的股票價格 。
設計一個算法計算出最大利潤。在滿足以下約束條件下,你可以儘可能地完成更多的交易(多次買賣一支股票):
- 你不能同時參與多筆交易(你必須在再次購買前出售掉之前的股票)。
- 賣出股票後,你無法在第二天買入股票 (即冷凍期爲 1 天)。
示例:
輸入: [1,2,3,0,2]
輸出: 3
解釋: 對應的交易狀態爲: [買入, 賣出, 冷凍期, 買入, 賣出]
Solution
O(n)空間複雜度的
class Solution {
//多次買賣一支股票 相當於買賣不限次數(最大price.length/2)
//冷凍期爲 1 天 所以狀態轉移時有i-2
public int maxProfit(int[] prices) {
//長度不夠 不能買賣
if(prices==null||prices.length<=1) return 0;
int[][]dp=new int[prices.length][2];
dp[0][0]=0;dp[0][1]=-prices[0];
//前一天沒持有股票OR 前一天持有股票了但今天賣了
dp[1][0]=Math.max(0,-prices[0]+prices[1]);
//前一天持有股票但今天還沒賣 OR 前一天沒持有股票,今天持有的
dp[1][1]=Math.max(-prices[0],0-prices[1]);// 其實就是在第一天和第二天取小的那個買(這裏取負數了所以有點難理解)
for(int i=2;i!=prices.length;i++) {
dp[i][0]=Math.max(dp[i-1][0],dp[i-1][1]+prices[i]);
// 昨天已經持有了OR前天已經前天前沒有持有(避開冷凍期),今天再買
dp[i][1]=Math.max(dp[i-1][1],dp[i-2][0]-prices[i]);
}
return dp[prices.length-1][0];
}
}
O(1)空間複雜度
class Solution {
//多次買賣一支股票 相當於買賣不限次數(最大price.length/2)
//冷凍期爲 1 天 所以狀態轉移時有i-2
public int maxProfit(int[] prices) {
//長度不夠 不能買賣
if(prices==null||prices.length<=1) return 0;
//還沒買呢, 怎麼可能擁有,爲負無窮,利於max操作
int yesterdayYes=Integer.MIN_VALUE;
int yesterdayNo=0;
//前天賣了或者前天之前買了,初始化爲0(因爲沒有持有股票)
int yesterday2No=0;
for(int i=0;i!=prices.length;i++) {
int tmp=yesterdayNo;
//昨天沒有持有OR前天沒有持有昨天也沒持有,今天買了
yesterdayYes=Math.max(yesterdayYes,yesterday2No-prices[i]);
yesterdayNo=Math.max(yesterdayNo,yesterdayYes+prices[i]);
yesterday2No=tmp;//昨天變前天了
}
return yesterdayNo;
}
}
714. 手續費:買賣股票的最佳時機含手續費
Difficulty: 中等
給定一個整數數組 prices
,其中第 i
個元素代表了第 i
天的股票價格 ;非負整數 fee
代表了交易股票的手續費用。
你可以無限次地完成交易,但是你每次交易都需要付手續費。如果你已經購買了一個股票,在賣出它之前你就不能再繼續購買股票了。
返回獲得利潤的最大值。
示例 1:
輸入: prices = [1, 3, 2, 8, 4, 9], fee = 2
輸出: 8
解釋: 能夠達到的最大利潤:
在此處買入 prices[0] = 1
在此處賣出 prices[3] = 8
在此處買入 prices[4] = 4
在此處賣出 prices[5] = 9
總利潤: ((8 - 1) - 2) + ((9 - 4) - 2) = 8.
注意:
0 < prices.length <= 50000
.0 < prices[i] < 50000
.0 <= fee < 50000
.
Solution
Language: Java
class Solution {
//無限次地完成交易
public int maxProfit(int[] prices, int fee) {
if(prices==null||prices.length<=1) return 0;
int[][]dp=new int[prices.length][2];
dp[0][0]=0;dp[0][1]=-prices[0]-fee;
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]-fee);
}
return dp[prices.length-1][0];
}
}
參考link(這是真大佬)
- https://github.com/labuladong/fucking-algorithm/blob/master/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92%E7%B3%BB%E5%88%97/%E5%9B%A2%E7%81%AD%E8%82%A1%E7%A5%A8%E9%97%AE%E9%A2%98.md