Problem
You are given an array prices
where prices[i]
is the price of a given stock on the ith
day.
Find the maximum profit you can achieve. You may complete at most two transactions.
Note: You may not engage in multiple transactions simultaneously (i.e., you must sell the stock before you buy again).
Note
A transaction is a buy & a sell. You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again).
Examples
Example 1:
Input: prices = [3,3,5,0,0,3,1,4]
Output: 6
Explanation: Buy on day 4 (price = 0) and sell on day 6 (price = 3), profit = 3-0 = 3.
Then buy on day 7 (price = 1) and sell on day 8 (price = 4), profit = 4-1 = 3.
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.
Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are engaging multiple transactions at the same time. You must sell before buying again.
Example 3:
Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transaction is done, i.e. max profit = 0.
Solution
Method 1 - Using Best Time to Buy and Sell Stock Only Once
We can use Best Time To Buy And Sell Stock 1 - only one transaction solution.
A straightforward approach is to evaluate each index i
and compute:
Max profit with at most two transactions =
MAX {max profit using one transaction in subarray prices[0..i] +
max profit using one transaction in subarray prices[i+1..n-1] }
for each i from 0 to n-1.
The maximum possible profit from one transaction can be calculated using an O(n)
algorithm.
The time complexity of this approach is O(n^2)
.
Method 2 - Divide and Conquer With Scanning From Left and Right to Calculate Max Profit
We can enhance Method 1
to develop an efficient O(n)
solution.
The concept is to record the maximum possible profit for each subarray and solve the problem in two phases:
- Create a table
profit[0..n-1]
and initialize all its values to 0. - Traverse
prices[]
from right to left, updatingprofit[i]
so it stores the maximum profit achievable from one transaction in the subarrayprices[i..n-1]
. - Traverse
prices[]
from left to right, updatingprofit[i]
so it represents the maximum profit achievable with two transactions in the subarrayprices[0..i]
. - Return
profit[n-1]
.
For step 1, keep track of the maximum price from right to left. For step 2, monitor the minimum price from left to right. Why traverse in reverse directions? This approach saves space by using the same array to store both the maximum profit from one transaction and the maximum profit from two transactions. After iteration i
, profit[0..i]
contains the maximum profit with two transactions, and profit[i+1..n-1]
contains the maximum profit with one transaction.
Below are implementations of this approach.
Here are the steps:
- First Phase: Calculate the maximum profit achievable from one transaction for each
i
in the subarrayprices[i..n-1]
.- Initialize
maxPrice
to the last price. - Traverse the array backwards to update
maxPrice
and calculate the maximum profit at each step.
- Initialize
- Second Phase: Calculate the maximum profit achievable with two transactions for each
i
in the subarrayprices[0..i]
.- Initialize
minPrice
to the first price. - Traverse the array forwards to update
minPrice
and calculate the maximum profit at each step.
- Initialize
- Final Result: The value
profit[n-1]
contains the maximum profit with at most two transactions.
Code
Java
public class Solution {
public int maxProfit(int prices[]) {
int n = prices.length;
int[] profit = new int[n];
// Calculate maximum profit with one transaction in the subarray
// prices[i..n-1]
int maxPrice = prices[n - 1];
for (int i = n - 2; i >= 0; i--) {
maxPrice = Math.max(maxPrice, prices[i]);
profit[i] = Math.max(profit[i + 1], maxPrice - prices[i]);
}
// Calculate the maximum profit with two transactions in the subarray
// prices[0..i]
int minPrice = prices[0];
for (int i = 1; i < n; i++) {
minPrice = Math.min(minPrice, prices[i]);
profit[i] =
Math.max(profit[i - 1], profit[i] + (prices[i] - minPrice));
}
return profit[n - 1];
}
}
Complexity
- ⏰ Time complexity:
O(n)
- 🧺 Space complexity:
O(n)
Method 3 - Using Two Separate Left and Array Matrix Instead of Using Profit Array
We will use two arrays in the proposed solution:
left[i]
= the maximum profit achievable by buying and selling in the interval[0, i]
. If selling happens at timej
(withj
in[0, i]
), the purchase should be at the minimum price between 0 andj
.right[i]
= the maximum profit achievable by buying and selling in the interval[i, n-1]
. If buying happens at timej
(withj
in[i, n-1]
), the sale should be at the maximum price betweenj
andn-1
.
To determine the best point i
to separate the two transactions, we need to maximize the combination left[i] + right[i]
. This involves evaluating all possible values of i
.
We use left[i]
to keep track of the maximum profit for transactions up to i
, and right[i]
for transactions after i
. Here’s an example to illustrate:
Prices: 1 4 5 7 6 3 2 9
left = [0, 3, 4, 6, 6, 6, 6, 8]
right = [8, 7, 7, 7, 7, 7, 7, 0]
sum = [8, 10, 11, 13, 13, 13, 13, 13]
Ans = sum[n-1] = 13
Code
Java
public int maxProfit(int[] prices) {
if (prices == null || prices.length < 2) {
return 0;
}
//highest profit in 0 ... i
int[] left = new int[prices.length];
int[] right = new int[prices.length];
// DP from left to right
left[0] = 0;
int min = prices[0];
for (int i = 1; i < prices.length; i++) {
min = Math.min(min, prices[i]);
left[i] = Math.max(left[i - 1], prices[i] - min);
}
// DP from right to left
right[prices.length - 1] = 0;
int max = prices[prices.length - 1];
for (int i = prices.length - 2; i >= 0; i--) {
max = Math.max(max, prices[i]);
right[i] = Math.max(right[i + 1], max - prices[i]);
}
int profit = 0;
for (int i = 0; i < prices.length; i++) {
profit = Math.max(profit, left[i] + right[i]);
}
return profit;
}
Method 4 - Multiple DP Solution Using at Most K Transcation
We can also approach this problem with dynamic programming. Suppose we are allowed to make up to k
transactions instead of just 2. Then this problem becomes - Best Time To Buy And Sell Stock 4 - at most k times