Stock Buy and Sell
LeetCode ↗ Java source ↗ Edit this article ↗
Jump to code:
Problem
You are given an array prices where prices[i] is the price of a stock on day i. You may buy on one day and sell on a later day, at most once. Return the maximum profit you can make. If no transaction is profitable (prices never rise), return 0: you simply do not trade.
This is LeetCode’s “Best Time to Buy and Sell Stock” (the first of the series). Only one transaction is allowed, which keeps the problem simple: the answer is the largest prices[j] - prices[i] with i < j.
Constraints: 0 ≤ n ≤ 10^5, 0 ≤ prices[i] ≤ 10^4. Because prices are non-negative and bounded, every difference fits in an int.
Example 1
prices = [7, 1, 5, 3, 6, 4]
answer = 5 // buy on day 1 (price 1), sell on day 4 (price 6)
Example 2
prices = [7, 6, 4, 3, 1]
answer = 0 // the price only falls, so do nothing
Example 3
prices = [2, 4, 1]
answer = 2 // buy at 2, sell at 4; the cheaper price 1 comes too late to sell after it
Approach 1: Brute force
Intuition. A transaction is a pair of days (i, j) with i < j. Try all of them and keep the largest difference. Starting the inner loop at i + 1 guarantees we sell strictly after we buy. Initialising the answer to 0 encodes “do not trade if nothing is profitable”.
Algorithm.
- Set
best = 0. - For every buy day
i, for every sell dayj > i:best = max(best, prices[j] - prices[i]). - Return
best.
static int bruteForce(int[] prices) {
int best = 0;
for (int i = 0; i < prices.length; i++) {
for (int j = i + 1; j < prices.length; j++) {
best = Math.max(best, prices[j] - prices[i]);
}
}
return best;
}
Complexity. Time O(n^2): about n^2 / 2 pairs. Space O(1).
For n = 10^5 that is 5 × 10^9 subtractions, far too slow.
Approach 2: Better, suffix maximum
Intuition. For a fixed buy day i, the best sell is the highest price on any later day. Instead of searching for it each time, precompute maxRight[i], the maximum of prices[i..n-1], in one right-to-left pass. Then the best profit for buying on day i is maxRight[i] - prices[i] (using maxRight[i] rather than maxRight[i + 1] is harmless, since selling on the buy day gives profit 0, which never beats the initial best = 0). One more pass takes the maximum over all i.
Algorithm.
- If the array is empty, return
0. - Build
maxRightfrom the right:maxRight[n-1] = prices[n-1],maxRight[i] = max(prices[i], maxRight[i+1]). - Return the maximum over
iofmaxRight[i] - prices[i], starting from0.
static int better(int[] prices) {
int n = prices.length;
if (n == 0) return 0;
int[] maxRight = new int[n]; // maxRight[i] = highest price on day i or later
maxRight[n - 1] = prices[n - 1];
for (int i = n - 2; i >= 0; i--) maxRight[i] = Math.max(prices[i], maxRight[i + 1]);
int best = 0;
for (int i = 0; i < n; i++) best = Math.max(best, maxRight[i] - prices[i]);
return best;
}
Complexity. Time O(n): two linear passes. Space O(n) for the auxiliary array.
This “precompute the best thing to my right” step is a general technique and worth recognising. Here it is not quite optimal because we can get the same information without storing it, by flipping the direction of the scan.
Approach 3: Optimal, track the minimum so far
Intuition. Flip the viewpoint: for a fixed sell day j, the best buy is the lowest price on any earlier day. Scanning left to right, that lowest price is just a running minimum, which needs one variable, not an array. At each day, either the price is a new minimum (a better future buy) or it is a candidate sell whose profit is price - minSoFar. Keep the best profit seen.
Why is this complete? Every transaction (i, j) has prices[i] ≥ minSoFar at the time we process j, so prices[j] - minSoFar ≥ prices[j] - prices[i]. The value we compute at day j is therefore at least as good as any transaction selling on day j, and it is itself a legal transaction (the minimum occurred on some earlier day, or on day j itself, in which case we are in the if branch and record nothing). Taking the maximum over all j covers every sell day.
Algorithm.
- Set
minSoFar = +∞,best = 0. - For every price
p: ifp < minSoFar, setminSoFar = p; otherwisebest = max(best, p - minSoFar). - Return
best.
static int optimal(int[] prices) {
int minSoFar = Integer.MAX_VALUE, best = 0;
for (int p : prices) {
if (p < minSoFar) minSoFar = p;
else best = Math.max(best, p - minSoFar);
}
return best;
}
Dry run on prices = [7, 1, 5, 3, 6, 4]:
| day | price | minSoFar after | profit if sold today | best |
|---|---|---|---|---|
| 0 | 7 | 7 | – (new minimum) | 0 |
| 1 | 1 | 1 | – (new minimum) | 0 |
| 2 | 5 | 1 | 4 | 4 |
| 3 | 3 | 1 | 2 | 4 |
| 4 | 6 | 1 | 5 | 5 |
| 5 | 4 | 1 | 3 | 5 |
Complexity. Time O(n): one pass. Space O(1): two integers.
Connection to Kadane’s algorithm
Define diff[i] = prices[i] - prices[i-1], the day-to-day change. The profit of buying on day i and selling on day j is the sum diff[i+1] + ... + diff[j]. So the maximum profit is the maximum subarray sum of diff (with the empty subarray allowed, giving 0). Running Kadane’s algorithm on diff is therefore an equivalent O(n) solution; the running minimum above is what that algorithm turns into once you simplify it. Seeing this makes the harder variants of the stock problem (several transactions, cooldown, fees) feel less like new problems and more like constrained versions of a familiar one.
Edge cases and pitfalls
- Empty array or a single day. No sale is possible; return
0. In the optimal version the loop records nothing because the only price becomes the minimum. The suffix-array version needs the explicitn == 0check before indexingprices[n - 1]. - Monotonically falling prices (
[7, 6, 4, 3, 1]). Every price is a new minimum,beststays0. - Flat prices (
[3, 3, 3]). The second3is not<the minimum, so it is treated as a sell with profit0; the answer stays0. Using<=in the condition would also be correct here, only the branch taken changes. - The global minimum comes last (
[2, 4, 1]). You cannot buy at1because there is no later day. The running-minimum method handles this naturally: the1updatesminSoFarbut no later price uses it. - Buying and selling on the same day is profit
0, so it never changes the answer; both linear methods are safe to compute it. - Overflow is not a concern with the stated bounds. If prices could be arbitrary
ints, computep - minSoFarinlong. - Multiple transactions are not allowed here; that is a different problem (Stock II). With one transaction the answer is just the best single rise.
Related problems
- Best time to buy and sell stock is this problem revisited in the DP section, as the base case of the stock series.
- Best time to buy and sell stock II allows unlimited transactions; the answer becomes the sum of all positive
diffvalues. - Kadane’s Algorithm is the maximum subarray sum this problem reduces to via day-to-day differences.
- Print subarray with maximum subarray sum (extended version of above problem) shows the bookkeeping you would add to report the buy and sell days.