Stock Span Problem Using Stack in DSA Rust - Time & Space Complexity
We want to understand how the time needed to solve the stock span problem changes as we get more stock prices.
How does the number of operations grow when we add more days of prices?
Analyze the time complexity of the following code snippet.
fn calculate_span(prices: &[i32]) -> Vec {
let mut stack: Vec = Vec::new();
let mut span = vec![0; prices.len()];
for i in 0..prices.len() {
while !stack.is_empty() && prices[*stack.last().unwrap()] <= prices[i] {
stack.pop();
}
span[i] = if stack.is_empty() { (i + 1) as i32 } else { (i - *stack.last().unwrap()) as i32 };
stack.push(i);
}
span
}
This code calculates the stock span for each day using a stack to keep track of previous higher prices.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The for-loop runs once for each price.
- Inside loop: The while-loop pops from the stack while the current price is higher.
- How many times: Each price index is pushed and popped at most once, so total stack operations are proportional to n.
As the number of prices grows, each price is processed once and may cause some stack pops, but overall the total operations grow roughly in a straight line.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 operations |
| 100 | About 200 operations |
| 1000 | About 2000 operations |
Pattern observation: The operations grow roughly in direct proportion to the number of prices.
Time Complexity: O(n)
This means the time to calculate spans grows linearly with the number of prices.
[X] Wrong: "The inner while-loop runs for every element, so the time is O(n²)."
[OK] Correct: Each element is pushed and popped only once, so the total work inside the while-loop across all iterations is just O(n), not O(n²).
Understanding this problem helps you master stack usage and efficient looping, skills that are valuable for many coding challenges.
"What if we used a simple nested loop without a stack? How would the time complexity change?"
Practice
Solution
Step 1: Understand the problem definition
The Stock Span Problem finds, for each day, the length of consecutive days ending on that day where prices are less than or equal to the current day's price.Step 2: Clarify what is calculated per day
It counts days backward until a higher price is found, including the current day.Final Answer:
The number of consecutive days ending with the current day with a price less than or equal to the current day's price -> Option AQuick Check:
Span counts consecutive lower/equal prices [OK]
- Confusing span with max or min price
- Counting all previous days instead of consecutive ones
- Calculating price differences instead of counts
Solution
Step 1: Identify the need for last-in-first-out access
The Stock Span Problem requires checking previous days' prices in reverse order, which fits stack behavior.Step 2: Choose Rust's stack implementation
Rust's Vec can be used as a stack with push and pop operations efficiently.Final Answer:
Vector (Vec) used as a stack -> Option BQuick Check:
Vec as stack fits LIFO for span calculation [OK]
- Using HashMap which is unordered
- Using LinkedList which is less efficient
- Using BinaryHeap which is for priority, not order
[100, 80, 60, 70, 60, 75, 85] using the Stock Span algorithm?Solution
Step 1: Calculate span for each price
Day 0: 100 -> span 1 (no previous)
Day 1: 80 < 100 -> span 1
Day 2: 60 < 80 -> span 1
Day 3: 70 > 60 -> span 2 (includes day 2)
Day 4: 60 < 70 -> span 1
Day 5: 75 > 60, 70 -> span 4 (includes days 4,3,2)
Day 6: 85 > 75, 70, 60, 80 -> span 6 (includes days 5,4,3,2,1)Step 2: Confirm final span array
The spans are [1, 1, 1, 2, 1, 4, 6].Final Answer:
[1, 1, 1, 2, 1, 4, 6] -> Option AQuick Check:
Span array matches calculated values [OK]
- Counting all previous days instead of consecutive
- Mixing up indices when calculating spans
- Assuming spans always increase
let mut stack: Vec<usize> = Vec::new();
let mut span = vec![0; prices.len()];
for i in 0..prices.len() {
while !stack.is_empty() && prices[*stack.last().unwrap()] < prices[i] {
stack.pop();
}
span[i] = if stack.is_empty() { i + 1 } else { i - stack.last().unwrap() };
stack.push(i);
}Solution
Step 1: Analyze span calculation line
span[i] = if stack.is_empty() { i + 1 } else { i - stack.last().unwrap() } uses stack.last().unwrap() without dereferencing, so it subtracts usize - &usize causing a type error or wrong calculation.Step 2: Correct dereferencing needed
It should be i - *stack.last().unwrap() to get the usize value from reference.Final Answer:
The span calculation subtracts without dereferencing the last stack element -> Option CQuick Check:
Dereference stack.last() for correct subtraction [OK]
- Using < instead of <= (not critical here)
- Not dereferencing stack.last() causing errors
- Initializing span with zeros is correct here
- Stack must store indices, not prices
Solution
Step 1: Understand streaming requirement
For streaming prices, we must update span without reprocessing all previous data.Step 2: Use stack with (price, span) pairs
Store each price with its span. When a new price arrives, pop all smaller prices and add their spans to current span, then push new pair.Step 3: Efficient update
This approach allows O(1) amortized time per new price, updating span incrementally.Final Answer:
Use a stack to store pairs of (price, span) and update span by accumulating popped spans -> Option DQuick Check:
Stack with (price, span) pairs for streaming [OK]
- Recalculating spans from scratch each time (inefficient)
- Using queue which doesn't support needed access pattern
- Using hash map which doesn't track order
Ready to prove it?
Pass the test and this pattern moves unknown
