NLeetCode Solutions
ProblemsPatternsStatistics
© 2026 Created by Trịnh Minh Nhật
GitHubLeetCode
Related posts
  • 746. Min Cost Climbing StairsEasy
  • 1. Two SumEasy
  • 14. Longest Common PrefixEasy
  • 26. Remove Duplicates from Sorted ArrayEasy
  • 27. Remove ElementEasy

121. Best Time to Buy and Sell Stock

Easy
ArrayDynamic Programming
Companies:
Tesla
Answered: Mar 28, 2026
View on LeetCode

Problem Description

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

You want to maximize 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.

Examples

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

Solutions

Complexity Analysis

Time Complexity:O(n)

We iterate through the prices array exactly once, performing O(1)O(1)O(1) work per element.

Space Complexity:O(1)

Only two variables, min_price and max_profit, are used regardless of input size.

We scan the prices array once, tracking the minimum price seen so far and the maximum profit achievable at each step. For each price, if it is lower than the current minimum we update min_price; otherwise, we compute the profit from buying at min_price and selling today, updating max_profit if it improves. A single pass suffices because any valid sell day already has access to the cheapest buy day before it through min_price.

Complexity Analysis

Time Complexity:O(n)

sell_day advances by one on every iteration and traverses the array at most once, giving O(n)O(n)O(n) total.

Space Complexity:O(1)

Only three integer variables are used, independent of input size.

We maintain two pointers, buy_day and sell_day, starting at indices 0 and 1. If the sell price exceeds the buy price we compute the profit and update max_profit; if not, a cheaper buy opportunity has been found so we advance buy_day to the current position. This correctly tracks the cheapest available buy point because any price lower than the current buy day would already have caused buy_day to move forward.

Complexity Analysis

Time Complexity:O(n)

Two independent linear passes are performed: one to build min_prefix and one to compute the maximum profit, each O(n)O(n)O(n).

Space Complexity:O(n)

The min_prefix array stores one value per input element, requiring O(n)O(n)O(n) extra space.

We first precompute a min_prefix array where min_prefix[i] holds the minimum price from day 0 through day i. With this array ready, the best profit from selling on day i is simply prices[i] - min_prefix[i], so a second pass finds the overall maximum. Separating the two concerns — finding the cheapest historical buy price and maximizing profit — makes the logic explicit, at the cost of O(n)O(n)O(n) extra space.

Complexity Analysis

Time Complexity:O(n)

We iterate through the prices array once from index 1 to n−1, performing O(1)O(1)O(1) work per step.

Space Complexity:O(1)

Only two variables, current_profit and max_profit, are maintained regardless of input size.

This approach reframes the problem as the maximum subarray sum on the array of daily price changes. By accumulating current_profit using consecutive differences (prices[i] - prices[i-1]), buying on day a and selling on day b is equivalent to summing the differences from a+1 to b — a classic Kadane's observation. Whenever current_profit turns negative, it is reset to 0, representing the decision to start a fresh transaction rather than carry a losing streak forward.

Complexity Analysis

Time Complexity:O(n)

A single pass through the array fills the dp table, performing O(1)O(1)O(1) work per element.

Space Complexity:O(n)

The dp array holds one entry per day, requiring O(n)O(n)O(n) extra space.

We define dp[i] as the maximum profit achievable by selling on or before day i. The recurrence dp[i] = max(dp[i-1], prices[i] - min_price) captures two choices: skip day i and carry forward the best result so far, or sell on day i using the cheapest historical buy price tracked in min_price. The answer is dp[n-1], the best profit over all days.

Complexity Analysis

Time Complexity:O(n)

We iterate through the prices array once, updating both state variables in O(1)O(1)O(1) per step.

Space Complexity:O(1)

Only two scalar state variables, hold and not_hold, are used regardless of input size.

We model the problem with two DP states: hold (the maximum cash after owning a stock) and not_hold (the maximum cash without owning a stock). On each day, hold transitions to the maximum of keeping the current position or buying today (−prices[i]), and not_hold transitions to the maximum of staying idle or selling today (hold + prices[i]). Because we only want one transaction, limiting the buy to the cheapest possible day is naturally captured by always taking max(hold, -prices[i]).

Complexity Analysis

Time Complexity:O(n)

We iterate through the prices array once from index 1 to n−1, performing O(1)O(1)O(1) work per element.

Space Complexity:O(1)

Only two variables, valley and max_profit, are used regardless of input size.

We initialize valley to the first price and scan forward, updating valley whenever a lower price is found and simultaneously checking whether selling today at prices[i] - valley beats the current best profit. Because valley always holds the minimum price seen to the left of any potential sell day, the optimal buy-sell pair is found in a single O(n)O(n)O(n) pass with no extra space.

Conclusion

ApproachRatingTime ComplexitySpace ComplexityAdvantagesDisadvantages
Greedy (Min Price Tracking)
O(n)O(n)O(n)O(1)O(1)O(1)Intuitive greedy approach, minimal variablesUses if/elif, so profit update is skipped on the same iteration as a new minimum
Two Pointers
O(n)O(n)O(n)O(1)O(1)O(1)Explicit buy/sell pointers make intent clearSlightly more complex than single-variable tracking
Prefix Minimum Array
O(n)O(n)O(n)O(n)O(n)O(n)Separates concerns clearly, easy to reason aboutRequires O(n)O(n)O(n) extra space for the prefix array
Kadane's Algorithm
O(n)O(n)O(n)O(1)O(1)O(1)Elegant reframe as max subarray sum; generalizes wellRequires understanding the price-difference transformation
Dynamic Programming
O(n)O(n)O(n)O(n)O(n)O(n)Explicit DP formulation, straightforward to extendRequires O(n)O(n)O(n) extra space for the dp array
State Machine DP
O(n)O(n)O(n)O(1)O(1)O(1)Generalizes naturally to multi-transaction variantsSlightly less intuitive for a single-transaction problem
Valley Tracking (Greedy)
O(n)O(n)O(n)O(1)O(1)O(1)Cleanest code — unconditional updates keep logic minimalFunctionally identical to Solution 1 with only minor stylistic differences

Both Solution 1: Greedy (Min Price Tracking) and Solution 7: Valley Tracking (Greedy) are optimal, both achieving O(n)O(n)O(n) time complexity and O(1)O(1)O(1) space complexity. They implement the same greedy insight — track the minimum price seen so far and update the best profit simultaneously — with minor stylistic differences: Solution 7 uses unconditional updates and initializes valley from prices[0], making it marginally cleaner, while Solution 1's if/elif structure is equally readable and equally efficient.


Previous70. Climbing Stairs
Next125. Valid Palindrome
Related posts
  • 746. Min Cost Climbing StairsEasy
  • 1. Two SumEasy
  • 14. Longest Common PrefixEasy
  • 26. Remove Duplicates from Sorted ArrayEasy
  • 27. Remove ElementEasy
On this page
13 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 # Initialize variables to track the minimum price and maximum profit
4 min_price = float('inf')
5 max_profit = 0
6
7 for price in prices:
8 if price < min_price: # Update the minimum price if the current price is lower
9 min_price = price
10 elif price - min_price > max_profit: # Update the maximum profit if the current price minus the minimum price is greater than the current maximum profit
11 max_profit = price - min_price
12
13 return max_profit
19 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 buy_day = 0
4 sell_day = 1
5 max_profit = 0
6
7 # Iterate through the price list with two pointers: buy_day and sell_day
8 while sell_day < len(prices):
9 # If the current sell price is greater than the buy price, calculate profit and update max_profit
10 if prices[buy_day] < prices[sell_day]:
11 profit = prices[sell_day] - prices[buy_day]
12 max_profit = max(max_profit, profit)
13 else:
14 # If the current sell price is less than or equal to the buy price, move the buy_day pointer to the sell_day
15 buy_day = sell_day
16
17 sell_day += 1
18
19 return max_profit
17 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 # Prefix array to store the minimum price up to each day
4 min_prefix = [0] * len(prices)
5 min_prefix[0] = prices[0]
6
7 # Compute the minimum price up to each day
8 for i in range(1, len(prices)):
9 min_prefix[i] = min(min_prefix[i - 1], prices[i])
10
11 max_profit = 0
12
13 # Compute the maximum profit by selling on each day
14 for i in range(len(prices)):
15 max_profit = max(max_profit, prices[i] - min_prefix[i])
16
17 return max_profit
14 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 current_profit = 0
4 max_profit = 0
5
6 for i in range(1, len(prices)):
7 # calculate profit for current day and add it to current_profit
8 current_profit += prices[i] - prices[i - 1]
9
10 # update max_profit if current_profit is greater, and reset current_profit to 0 if it becomes negative
11 max_profit = max(max_profit, current_profit)
12 current_profit = max(current_profit, 0)
13
14 return max_profit
13 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 min_price = prices[0]
4 dp = [0] * len(prices)
5
6 for i in range(1, len(prices)):
7 min_price = min(min_price, prices[i])
8
9 # The maximum profit that can be achieved by selling on day i is the maximum of:
10 dp[i] = max(dp[i - 1], prices[i] - min_price)
11
12 # The maximum profit that can be achieved by selling on the last day is the answer.
13 return dp[-1]
11 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 hold = -prices[0] # We start with the first price as if we bought the stock on the first day
4 not_hold = 0 # We start with 0 profit as we haven't sold any stock yet
5
6 for i in range(1, len(prices)):
7 hold = max(hold, -prices[i]) # We either keep holding the stock or we buy it today (which means we subtract today's price from our profit)
8 not_hold = max(not_hold, hold + prices[i]) # We either keep not holding the stock or we sell it today (which means we add today's price to our profit)
9
10 # The maximum profit will be in the not_hold state at the end, as we want to have sold the stock to realize the profit
11 return not_hold
11 lines
1class Solution:
2 def maxProfit(self, prices: List[int]) -> int:
3 valley = prices[0] # We start with the first price as the valley (the lowest price we've seen so far)
4 max_profit = 0 # We start with 0 profit as we haven't sold any stock
5
6 for i in range(1, len(prices)):
7 valley = min(valley, prices[i]) # Update the valley if we find a lower price
8 max_profit = max(max_profit, prices[i] - valley) # Update the max profit if we find a higher profit by selling at the current price
9
10 # The maximum profit will be the maximum difference between the current price and the lowest price (valley) we've seen so far
11 return max_profit