Skip to content
Skilled.dev

Free lesson walkthrough · Python

Best stock profit in Python: one transaction

Find the best single chronological stock trade with one scan. Follow the Python implementation, its correctness argument, worked example, complexity, and boundary tests.

4 min read · Worked code, edge cases, and complexity

Given prices in chronological order, return the largest profit from at most one purchase followed by one sale. Buying must happen before selling. Choosing no trade is allowed, so an empty list, one price, or a falling market returns zero. For [7, 1, 5, 3, 6, 4], buy at 1 and sell later at 6 for a profit of 5.

A direct solution tries every purchase day and every later sale day. That establishes the contract clearly, but considers O(n²) pairs. When examining a particular sale day, the best purchase is always the lowest price among earlier days. Every more expensive earlier purchase yields no greater profit for that same sale.

Keep that minimum as a running value. Also keep the best profit found so far, initially zero. For each price, compare its difference from the previous minimum with the current best profit, then update the minimum. This order makes the chronology easy to explain. Allowing a zero-length trade on the same day would also yield only zero, but cannot create a positive profit.

Trace the two pieces of state

At price 7, the minimum is 7 and the best profit stays zero. Price 1 cannot produce a profitable sale, but becomes the new minimum. Price 5 gives profit 4. Price 3 gives profit 2, so the best remains 4. Price 6 improves the best to 5. The final price 4 gives only 3.

The minimum does not reset after finding a profitable trade. A later sale can still use the same earlier purchase. Nor should the algorithm add positive differences between adjacent days. Adding those differences permits several separate trades, which violates the one-purchase, one-sale limit.

Prove that the scan keeps the best trade

Before considering a sale day, the stored minimum is the smallest price in the already visited prefix. Subtracting it therefore finds the best trade whose sale occurs on the current day. Taking the maximum with the stored profit keeps the best trade over every sale day considered so far.

At the end, every legal sale day has been examined. Each was paired with its best legal earlier purchase, so their maximum is the global optimum. Initializing profit to zero handles the option of making no trade. Returning a negative difference would force a loss that the problem does not require.

Handle initialization and numeric bounds

Some implementations return early for fewer than two prices; others choose a sentinel minimum or let the empty loop preserve zero. Read the actual initialization in the code before adapting it. Accessing the first element of an empty array is an error in several languages, even though the mathematical recurrence handles empty input naturally.

Price subtraction can overflow a narrow integer if a broader contract admits extreme signed values. The course uses bounded prices; a financial application would also need a clear decimal or integer-unit policy. This algorithm optimizes a sequence of supplied values. It does not account for fees, short selling, taxes, multiple positions, or trading predictions.

Costs and diagnostic tests

The scan takes O(n) time and keeps two numeric values, so auxiliary space is O(1). It does not sort or change the input. Sorting would destroy chronology: a low price after the highest price cannot fund a sale that already happened.

Test [9, 7, 4] for zero, [2, 8] for six, [8, 2] for zero, and [3, 3, 3] for zero. Include a best sale before the final day and a new minimum near the end. Compare short generated arrays with the exhaustive pair search. As a follow-up, ask how the state changes when two complete transactions are allowed; the single minimum alone no longer captures all legal histories.

Python implementation

The early length check handles empty and singleton lists before reading the first price. The scan retains only the minimum and best profit.

Python entries use the function or class API shown in the lesson starter. Lists and dictionaries are mutable, and assignment shares those objects. Integer arithmetic has arbitrary precision, although the course input and output schema still has bounded integer types. Recursive solutions use Python call frames; an iterative rewrite is appropriate when input depth can exceed the interpreter's recursion limit.

def get_max_profit(prices):
    if len(prices) < 2:
        return 0

    max_profit = 0
    min_price = prices[0]

    for price in prices:
        profit_at_current_day = price - min_price
        max_profit = max(max_profit, profit_at_current_day)
        min_price = min(price, min_price)

    return max_profit

Practice this implementation

Open Best Time to Buy and Sell Stock to run the implementation against the course tests, then change one boundary condition and explain the first failing case. The editor supplies the starter signature and any node types or helper APIs used above. Copy the implementation into that environment; the snippet is the solution body, without the input parser or test driver. Your saved progress belongs to the original lesson.

For more Python conventions and practice problems, use the Python interview guide.

Try the exercise

Write your own implementation in the course editor. This walkthrough belongs to the same lesson; your course progress stays with that exercise. Exercise access follows the course’s access settings.