Home Leetcode

121. Best Time to Buy and Sell Stock

You are given an array prices where prices[i] is the price of a given stock on the ith day.

You want to maximise your profit by choosing a single day to buy one stock and choosing a different day in the future to sell that stock.

Return the maximum profit you can achieve from this transaction. If you cannot achieve any profit, return 0.

Constraints:

Let (i,j)(i, j) represent buying on day ii and selling on day jj, and pip_i be the price on day ii. The brute force solution requires us to check every pair (i,j)(i, j) and calculate the maximum profit from that. We can reduce the solution space, however, if we consider a few insights:

We can thus start our search from the indices (0,1)(0, 1)—buy on the first day, and sell on the next. If there is a profit, then we can check selling on the subsequent day, (0,2)(0, 2), and so on.

In general, if (0,j)(0, j) results in a loss, why don’t we need to check (i,k)(i, k) for some 0<i<j<k0 < i < j < k and can move straight to checking (j,k)(j, k)? Since the algorithm has moved the selling day past ii to kk, that means that (0,i)(0, i) has resulted in a profit—in other words, pip0p_{i} \geq p_0. Since (0,j)(0, j) is a loss, we have pip0>pjp_i \geq p_0 > p_j. Thus, pkpipkp0<pkpjp_k - p_{i} \leq p_k - p_0 < p_k - p_j, so (i,k)(i, k) always underperforms compared to (j,k)(j, k).

def maxProfit(self, prices: List[int]) -> int:
	buy = 0
	max_profit = 0
	
	for sell in range(1, len(prices)):
		profit = prices[sell] - prices[buy]
		if profit < 0:
			# (sell, k) always outperforms (buy, k) for subsequent k
			buy = sell
		else:
			# update max_profit if needed; otherwise, keep checking 
			max_profit = max(max_profit, profit)
	
	return max_profit

Another way to think about this is that we always buy at the lowest price we have seen. Notice that checking if profit < 0 is the same thing as checking if prices[sell] < prices[buy]. If so, we update buy = sell. And since buying at the lowest price will outperform buying at any other price seen before, we won’t miss any bigger trades.

Time Complexity

Notice the while loop is dependant on the value of sell, and not buy, and that sell always increments by 1 at every iteration. Therefore, a maximum of nn iterations are performed, so the algorithm has a worst case runtime in O(n)\mathcal{O}(n).

Space Complexity

We don’t store any complex structures—the only variables declared are constant integers, so the space complexity is constant—O(1)\mathcal{O}(1).