121. Best Time to Buy and Sell Stock
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Example 1:
Input: prices = [7,1,5,3,6,4]
Output: 5
Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 6-1 = 5.
Note that buying on day 2 and selling on day 1 is not allowed because you must buy before you sell.
Example 2:
Input: prices = [7,6,4,3,1]
Output: 0
Explanation: In this case, no transactions are done and the max profit = 0.
Constraints:
1 <= prices.length <= 105
0 <= prices[i] <= 104
Intuition
First thoughts
In order to solve this question in a brute-force way, we’ll have to:
- loop through all the elements and for each element (i.e. selecting the current element as our buying price):
- we need to check against all the possible selling points (i.e. all the prices after our chosen buying price).
- calculate if we are going to make any profit with the pair of chosen
buy price
andsell price
. Record that in higher level variable (let’s call itmax_profit
- only need to store the maximum obtained so far)
- calculate if we are going to make any profit with the pair of chosen
- we need to check against all the possible selling points (i.e. all the prices after our chosen buying price).
- by end of the first loop, we’ll have our answer recorded in the higher level variable
max_profit
.
This approach has:
- run time complexity of O(n2) (as we have nested loops).
- space complextity of O(1) (since our storage needs doesn’t grow with input size i.e. size of the allocated variable
max_profit
is going to be same for any input size).
Further thoughts
Can we do better than O(n2)?
- In our brute-force approach, for each potential buying price, we are eventually looking for the best possible selling price. The inefficiency in this approach is that it keeps looking for the selling price,
even if our current profit becomes 0 or -ve at certain point
. We know for sure that for such cases, our chosen buying price can’t really be the solution to our problem as a better buying price exists at later stage due to which the profit becomes either 0 or -ve. - Let’s consider an example
prices = [7,2,5,1,3,6,4]
. Assume that we are atday 2 (price=2, index=1)
. Now, while looking for potential selling prices, at some point in time, we would considerselling at day 4 (price=1, index=3)
. At that point the current profit would have been become -1. Which essentially means that there is no point in considering our current buying price (index=1) because either same or better buying price exists at later stage. So, we can safely ignore the intermediate buying prices and directly move to index=3. - From there on, we can keep looking for the optimal selling price:
- till we run out of all potential prices.
- At each valid selling point, we record the candidate result in the higher level variable
max_profit
(only if we find any better result from the previously computed candidates). - If we stumble upon a situation where we encounter
-ve profit
, we ignore looking any further and we move to next possible buying price.
Final approach
- Initialize a varible namely
max_profit
with current value as0
. - Loop through all the elements of given array.
- keep looking for potential selling prices, till the computed profit so far is
>= 0
. - otherwise, (i.e. current profit gets -ve):
- Move to the current selling price being considered.
- while we keep getting +ve result store the max of
max_profit
andcurrent profit
in the variablemax_profit
.
- keep looking for potential selling prices, till the computed profit so far is
- By end of the main loop we would have gotten our result in the variable
max_profit
. Time to just return it. - Time complexity: O(n) (since we only process all the elements at max 2 times).
- Space complexity: O(1) (since our auxiliary storage
max_profit
doesn’t grow with input size).
Python code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Solution:
def maxProfit(self, prices: List[int]) -> int:
max_profit, buy_price_idx, sell_price_idx = 0, 0, 0
while sell_price_idx < len(prices):
current_profit = prices[sell_price_idx] - prices[buy_price_idx]
if current_profit >= 0:
max_profit = max(max_profit, current_profit)
sell_price_idx += 1
else:
buy_price_idx = sell_price_idx
return max_profit
# Alternative thinking model in terms of sliding window
class Solution:
def maxProfit(self, prices: List[int]) -> int:
buy, maxProfit = 0, 0
for sell in range(len(prices)):
# expand
# record candidate result if window is valid
if prices[buy] <= prices[sell]:
maxProfit = max(maxProfit, prices[sell]-prices[buy])
# shrink
if prices[sell] < prices[buy]:
buy = sell
return maxProfit