901. Online Stock Span - Explanation

Problem Link

Description

Design an algorithm that collects daily price quotes for some stock and returns the span of that stock's price for the current day.

The span of the stock's price in one day is the maximum number of consecutive days (starting from that day and going backward) for which the stock price was less than or equal to the price of that day.

  • For example, if the prices of the stock in the last four days is [7,2,1,2] and the price of the stock today is 2, then the span of today is 4 because starting from today, the price of the stock was less than or equal 2 for 4 consecutive days.
  • Also, if the prices of the stock in the last four days is [7,34,1,2] and the price of the stock today is 8, then the span of today is 3 because starting from today, the price of the stock was less than or equal 8 for 3 consecutive days.

Implement the StockSpanner class:

  • StockSpanner() Initializes the object of the class.
  • int next(int price) Returns the span of the stock's price given that today's price is price.

Example 1:

Input: ["StockSpanner", "next", "next", "next", "next", "next", "next", "next"]
[[], [100], [80], [60], [70], [60], [75], [85]]

Output: [null, 1, 1, 1, 2, 1, 4, 6]

Explanation:
StockSpanner stockSpanner = new StockSpanner();
stockSpanner.next(100); // return 1
stockSpanner.next(80); // return 1
stockSpanner.next(60); // return 1
stockSpanner.next(70); // return 2
stockSpanner.next(60); // return 1
stockSpanner.next(75); // return 4, because the last 4 prices (including today's price of 75) were less than or equal to today's price.
stockSpanner.next(85); // return 6

Constraints:

  • 1 <= price <= 100,000
  • At most 10,000 calls will be made to next.


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Stack Data Structure - Understanding LIFO operations (push, pop, peek) and when to use stacks
  • Monotonic Stack - A stack that maintains elements in strictly increasing or decreasing order for efficient range queries
  • Amortized Analysis - Understanding why each element is pushed and popped at most once across all operations

1. Brute Force

Intuition

The stock span is the number of consecutive days (including today) where the price was less than or equal to today's price. The simplest approach is to store all prices and, for each new price, look backward through the history counting days until we find a higher price.

Algorithm

  1. Maintain an array arr to store all prices seen so far.
  2. For each next(price) call:
    • Append the new price to arr.
    • Start from the second-to-last index and move backward.
    • Count consecutive days where arr[i] <= price.
    • Stop when we find a price greater than the current one or reach the beginning.
    • Return the count (current position minus the stopping index).
class StockSpanner:

    def __init__(self):
        self.arr = []

    def next(self, price: int) -> int:
        self.arr.append(price)
        i = len(self.arr) - 2
        while i >= 0 and self.arr[i] <= price:
            i -= 1
        return len(self.arr) - i - 1

Time & Space Complexity

  • Time complexity: O(n2)O(n ^ 2)
  • Space complexity: O(n)O(n)

Where nn is the number of function calls.


2. Monotonic Decreasing Stack

Intuition

The brute force approach repeatedly scans the same elements. We can avoid this by using a monotonic decreasing stack that stores pairs of (price, span).

When a new price arrives, we pop all entries from the stack that have prices less than or equal to the current price. The span of the current day is 1 (for today) plus the sum of spans of all popped entries. This works because those popped entries represent consecutive days that are now "covered" by the current higher price.

The stack remains in decreasing order of prices, so each element is pushed and popped at most once across all operations.

Algorithm

  1. Initialize an empty stack that stores pairs of (price, span).
  2. For each next(price) call:
    • Start with span = 1 (counting today).
    • While the stack is not empty and the top price is less than or equal to the current price:
      • Pop the top element and add its span to the current span.
    • Push (price, span) onto the stack.
    • Return span.
class StockSpanner:

    def __init__(self):
        self.stack = []  # pair: (price, span)

    def next(self, price: int) -> int:
        span = 1
        while self.stack and self.stack[-1][0] <= price:
            span += self.stack[-1][1]
            self.stack.pop()
        self.stack.append((price, span))
        return span

Time & Space Complexity

  • Time complexity: O(n)O(n)
  • Space complexity: O(n)O(n)

Where nn is the number of function calls.


Common Pitfalls

Using Strictly Less Than Instead of Less Than or Equal

The span includes all consecutive days where prices are less than or equal to the current price, not strictly less than. Using < instead of <= will undercount the span when previous days have the same price as today.

Forgetting to Include the Current Day in the Span

The span always includes the current day itself. When initializing the span counter, start with 1 (for today) before adding spans from popped elements. Starting with 0 will result in spans that are off by one.

Storing Only Prices Without Spans

Storing just prices on the stack requires recounting spans on every query, leading to O(n) per operation. The stack should store pairs of (price, span) so that when an element is popped, its accumulated span can be added directly to the current span in O(1) time.