NO.121 买卖股票的最佳时机 简单 、NO.122 买卖股票的最佳时机II 简单 、NO.123 买卖股票的最佳时机III 困难 、NO.188 买卖股票的最佳时机IV 困难
NO.121 买卖股票的最佳时机 简单
思路一:暴力法 没什么好说的,双重循环计算所有元素两两组合相减的结果,取最大。
1 | public int maxProfit(int[] prices) { |
时间复杂度:O(n^2)
思路二:优化暴力法到一次遍历 买卖股票从第二天开始我们每天都会”后悔”:后悔没有在之前的最低点进行买入,只有这样我们的收益才会最大化。
由此可见,我们想要当天利益最大化,只需要在过去的某个最低点买入股票就好。所以我们只需要记录曾经出现过的最低点就好。
1 | public int maxProfit(int[] prices) { |
时间复杂度:O(n)
从NO.121题不难看出:买股票的最佳时机是曾经!股市有风险,入股需谨慎!(狗头)
单纯的解答本题是比较简单的,但是买卖股票可以算作是一个系列的经典问题,在leetcode上就有本题一系列的变种问题:买卖股票的最佳时机、买卖股票的最佳时机II、买卖股票的最佳时机III、买卖股票的最佳时机IV、买卖股票的最佳时机含冷冻期、买卖股票的最佳时机含手续费。
虽然这些题有难有易,但是既然是一类问题,就有这一些通用的方法。
这六个问题都是由第四个问题简化变种而来的,第四题相较于本题多了一个参数k,限制只能进行k次交易;第一题也就是本题是只进行一次交易,相当于 k = 1;第二题是不限交易次数,相当于 k = +infinity(正无穷);第三题是只进行 2 次交易,相当于 k = 2;剩下两道也是不限次数,但是加了交易「冷冻期」和「手续费」的额外条件,其实就是第二题的变种。
NO.122 买卖股票的最佳时机II 简单
思路一:贪心算法 姊妹题NO.121,区别在于NO.121题只允许交易一次(一次买入、一次卖出),而本题没有交易次数的限制。
所以想到贪心算法,因为我们是”上帝视角”知道”未来股价”,所以只要赚钱我们每天都进行交易。
所谓赚钱就是:”明天i+1股价”-“今天i股价”>0,这种情况今天就买入;否则就是赔钱或者不赚钱的情况,不进行交易。
1 | public int maxProfit(int[] prices) { |
时间复杂度:O(n)
思路二:动态规划 贪心算法已经很好地解决了这个问题,但是我们还是要带入一下DP解法,为后序的姊妹题NO.122买卖股票的最佳时机III、以及等等很多其他的XXX买卖股票问题都是基于本题进行变化的,无非是在本题的基础上增加更多的限制条件。
dp[i][j]的含义:i表示第i天,j表示持股状态(0表示不持股,1表示持股)。dp[i][j]整体就表示第i天持股/不持股时手里的钱。
初始化:dp[0][0]=0,刚开始不赔不赚;dp[0][1]= -prices[0],只支出了。
状态转移:dp[i][0]=Max(dp[i-1][0],dp[i-1][1]+prices[i])
,即第i天不持股时手里钱最多为MAX(i-1天不持股i天继续不持股,i-1天持股+i天以当日价格卖掉股票);
同理dp[i][1]=Max(dp[i-1][0]-prices[i],dp[i-1][1])
,即第i天持股时手里钱最多为MAX(i-1天不持股-i天以当日价格买入股票,i-1天持股i天继续持股)。
1 | public int maxProfit(int[] prices) { |
时间复杂度:O(n)
NO.123 买卖股票的最佳时机III 困难
思路一:动态规划 NO.121是只能买卖一次股票,NO.122是任意买卖股票,而本题是只能买卖股票两次。
相较于前两题的状态,本题又增加了一个交易次数的状态k。
dp[i][j][k]的含义:第i天的利润,j表示持股状态(0不持股,1持股),k交易次数(卖出次数0~2)。所以每天有六种状态,思路就是从每天的六种状态中找到利润最大的。
初始化:dp[0][0][0]=dp[0][0][1]=dp[0][0][2]=0
,啥也没干;dp[0][1][0]=dp[0][1][1]=dp[0][1][2]=-prices[0]
,第一天持股只有支出。
状态转移:dp[i][0][0]=dp[i-1][0][0]
,第i天不持股交易次数0,第i-1天一定未持股未交易,维持现状;
dp[i][0][1]=MAX(dp[i-1][0][1],dp[i-1][1][0]+prices[i])
,第i天不持股1次交易,维持第i-1天的状态,或者第i-1天持股未交易第i天卖出。
dp[i][0][2]=MAX(dp[i-1][0][2],dp[i-1][1][1]+prices[i])
,第i天不持股2次交易,维持第i-1天的状态,或者第i-1天持股1次交易第i天卖出。
dp[i][1][0]=MAX(dp[i-1][1][0],dp[i-1][0][0]-prices[i])
,第i天持股未交易,维持第i-1天的状态,或者第i-1天不持股未交易第i天买入。
dp[i][1][1]=MAX(dp[i-1][1][1],dp[i-1][0][1]-prices[i])
,第i天持股1次交易,维持第i-1天的状态,或者第i-1天不持股1次交易第i天买入。
dp[i][1][2]=dp[i][1][2]
,第i天持股2次交易,已经到达交易限制,但是手中还持股,不需要考虑一定不能利润最大化。
最终的结果一定是在不持股的三种状态中进行比较。
1 | public int maxProfit(int[] prices) { |
时间复杂度:O(n) 空间O(n)虽然是三维数组,但2和3是常数。
NO.188 买卖股票的最佳时机IV 困难
思路一:动态规划 在上一题的基础上两次交易,变味了 k 次交易。但是需要考虑的状态,仍然是三个,第 i 天、j 持股状态、k 进行过的交易次数。
虽然 k 从上一题的常量 2 变为了本题的变量,但是 dp 数组的含义不变。但是本题如果使用三维数组会因为 k 值过大,导致内存超出。
所以采用两个一维数组 dpi0[k+1] 和 dpi1[k+1] 表示第 i 天持股(1)或不持股(0)时,不同交易次数下的利润。(上一题也可以采用这个方法,进行空间优化)
实现上,由于本题 k 不是上一题中的常量 2 ,无法手动写死每种情况,所以采用一个 for 循环即可。
1 | public int maxProfit(int k, int[] prices) { |
时间复杂度:O(n*k) n 共n天
空间复杂度:O(k)
本人菜鸟,有错误请告知,感激不尽!