Problem Statement
You are given an integer array prices
 where prices[i]
 is the price of a given stock on the ith
 day.
On each day, you may decide to buy and/or sell the stock. You can only hold at most one share of the stock at any time. However, you can buy it then immediately sell it on the same day.
Find and return the maximum profit you can achieve.
Example 1: Input: prices = [7,1,5,3,6,4] Output: 7 Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. Total profit is 4 + 3 = 7.
Example 2: Input: prices = [1,2,3,4,5] Output: 4 Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. Total profit is 4.
Example 3: Input: prices = [7,6,4,3,1] Output: 0 Explanation: There is no way to make a positive profit, so we never buy the stock to achieve the maximum profit of 0.
Constraints:
1 <= prices.length <= 3 * 10^4
0 <= prices[i] <= 10^4
Solution:
1. Recursion
class Solution {
public int maxProfit(int[] prices) {
return trade(prices, 0, false, 0);
}
public int trade(int[] prices, int day, boolean hasStock, int profit){
if(day == prices.length){
return profit;
}
//Choice 1: Do nothing
int doNothing = trade(prices, day + 1, hasStock, profit);
//Choice 2: Buy the stock if not holding
int buy = 0;
if(!hasStock){
buy = trade(prices, day + 1, true, profit - prices[day]);
}
//Choice 3: Sell the stock if holding
int sell = 0;
if(hasStock){
sell = trade(prices, day + 1, false, profit + prices[day]);
}
return Math.max(doNothing, Math.max(buy, sell));
}
}
2. Recursion and Memoization:
class Solution {
public int maxProfit(int[] prices) {
int[][] dp = new int[prices.length][2];
for (int i = 0; i < prices.length; i++) {
Arrays.fill(dp[i], -1);
}
//hasStock = 0 : don't have stock
//hasStock = 1 : have stock
return trade(prices, 0, 0, dp);
}
public int trade(int[] prices, int day, int hasStock, int[][] dp){
if(day == prices.length){
return 0;
}
if(dp[day][hasStock] != -1){
return dp[day][hasStock];
}
//Choice 1: Do nothing
int doNothing = trade(prices, day + 1, hasStock, dp);
//Choice 2: Buy the stock if not holding
int buy = 0;
if(hasStock == 0){
buy = trade(prices, day + 1, 1, dp) - prices[day];
}
//Choice 3: Sell the stock if holding
int sell = 0;
if(hasStock == 1){
sell = trade(prices, day + 1, 0, dp) + prices[day];
}
return dp[day][hasStock] = Math.max(doNothing, Math.max(buy, sell));
}
}
3. Tabulation:
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][0] represents the maximum profit at the end of day i without holding a stock
// dp[i][1] represents the maximum profit at the end of day i holding a stock
int[][] dp = new int[n][2];
// Base case: At the beginning (day 0), the maximum profit without holding a stock is 0
dp[0][0] = 0;
// Base case: At the beginning (day 0), the maximum profit holding a stock is -prices[0]
dp[0][1] = -prices[0];
// Iterate through days starting from day 1
for (int i = 1; i < n; i++) {
// If not holding a stock on day i, we can either do nothing (stay not holding) or buy a stock
dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] + prices[i]);
// If holding a stock on day i, we can either do nothing (stay holding) or sell the stock
dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] - prices[i]);
}
// The maximum profit at the end of the last day without holding a stock is the final answer
return dp[n - 1][0];
}
}
3. Tabulation with space optimization:
class Solution {
public int maxProfit(int[] prices) {
int n = prices.length;
// dp[i][0] represents the maximum profit at the end of day i without holding a stock
// dp[i][1] represents the maximum profit at the end of day i holding a stock
int[] prev = new int[2];
int[] curr = new int[2];
// Base case: At the beginning (day 0), the maximum profit without holding a stock is 0
prev[0] = 0;
// Base case: At the beginning (day 0), the maximum profit holding a stock is -prices[0]
prev[1] = -prices[0];
// Iterate through days starting from day 1
for (int i = 1; i < n; i++) {
// If not holding a stock on day i, we can either do nothing (stay not holding) or buy a stock
curr[0] = Math.max(prev[0], prev[1] + prices[i]);
// If holding a stock on day i, we can either do nothing (stay holding) or sell the stock
curr[1] = Math.max(prev[1], prev[0] - prices[i]);
prev = curr.clone();
}
// The maximum profit at the end of the last day without holding a stock is the final answer
return prev[0];
}
}
4. Greedy
Account for only increasing sequences
class Solution {
public int maxProfit(int[] prices) {
int profitSoFar = 0, buyDate = 0, sellDate = 0;
for(int day = 1; day < prices.length; day++){
if(prices[day] >= prices[day-1]){
sellDate++;
} else{
profitSoFar += prices[sellDate] - prices[buyDate];
sellDate = day;
buyDate = day;
}
}
//This the loop is necessary to account for the last potential profitable
//trade that may not have been settled within the loop.
profitSoFar += prices[sellDate] - prices[buyDate];
return profitSoFar;
}
}