Next Greater Element Using Stack in DSA Rust - Time & Space Complexity
We want to understand how the time needed to find the next greater element changes as the input list grows.
How does the algorithm handle larger lists efficiently?
Analyze the time complexity of the following code snippet.
fn next_greater_element(nums: &[i32]) -> Vec {
let mut stack = Vec::new();
let mut result = vec![-1; nums.len()];
for i in (0..nums.len()).rev() {
while let Some(&top) = stack.last() {
if top <= nums[i] {
stack.pop();
} else {
break;
}
}
if let Some(&top) = stack.last() {
result[i] = top;
}
stack.push(nums[i]);
}
result
}
This code finds the next greater number for each element in the list using a stack.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: A single loop running backward through the list.
- How many times: The loop runs once for each element (n times).
- Stack operations: Each element is pushed and popped at most once.
As the list grows, each element is processed once, and stack operations happen at most once per element.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 operations (push/pop per element) |
| 100 | About 200 operations |
| 1000 | About 2000 operations |
Pattern observation: The operations grow roughly in a straight line with input size.
Time Complexity: O(n)
This means the time to find next greater elements grows linearly with the list size.
[X] Wrong: "Because there is a nested while loop inside the for loop, the time complexity must be O(n²)."
[OK] Correct: Each element is pushed and popped only once, so the total work inside the while loop across all iterations is still proportional to n, not n squared.
Understanding this linear time stack approach shows you can solve problems efficiently by avoiding repeated work, a key skill in coding interviews.
What if we changed the input to a circular list where the search wraps around? How would the time complexity change?
Practice
Solution
Step 1: Understand the role of stack in the problem
The stack stores elements that are waiting to find their next greater element as we scan the array.Step 2: Clarify what the stack does not do
The stack does not sort or store final output; it only helps track elements pending next greater element discovery.Final Answer:
To keep track of elements for which we haven't found the next greater element yet -> Option AQuick Check:
Stack tracks pending elements = A [OK]
- Thinking stack sorts the array
- Confusing stack with output storage
- Assuming stack reverses the array
Solution
Step 1: Identify correct Rust syntax for empty vector
In Rust, an empty vector is created with Vec::new() and mutable if we want to push elements.Step 2: Check each option
let mut stack: Vec<i32> = Vec::new(); uses correct syntax with mut and Vec::new(). Options A, B, and C are invalid Rust syntax.Final Answer:
let mut stack: Vec<i32> = Vec::new(); -> Option BQuick Check:
Vec::new() with mut = D [OK]
- Omitting mut when pushing elements
- Using incorrect Vec initialization syntax
- Confusing Vec::new() with other methods
let nums = vec![4, 5, 2, 25];
let mut stack: Vec<usize> = Vec::new();
let mut result = vec![-1; nums.len()];
for i in 0..nums.len() {
while !stack.is_empty() && nums[*stack.last().unwrap()] < nums[i] {
let idx = stack.pop().unwrap();
result[idx] = nums[i];
}
stack.push(i);
}
println!("{:?}", result);Solution
Step 1: Trace the loop and stack operations
Start with empty stack and result [-1, -1, -1, -1]. - i=0: stack empty, push 0 (stack=[0]) - i=1: nums[0]=4 < nums[1]=5, pop 0, result[0]=5, push 1 (stack=[1]) - i=2: nums[1]=5 > nums[2]=2, push 2 (stack=[1,2]) - i=3: nums[2]=2 < nums[3]=25, pop 2, result[2]=25 nums[1]=5 < 25, pop 1, result[1]=25, push 3 (stack=[3])Step 2: Finalize result array
Stack has index 3 left, result[3] remains -1. Final result is [5, 25, 25, -1].Final Answer:
[5, 25, 25, -1] -> Option CQuick Check:
Next greater elements = [5, 25, 25, -1] [OK]
- Not updating result when popping from stack
- Pushing indices instead of values
- Confusing indices and values in comparisons
let nums = vec![2, 7, 3, 5, 4, 6, 8];
let mut stack: Vec<i32> = Vec::new();
let mut result = vec![-1; nums.len()];
for i in 0..nums.len() {
while !stack.is_empty() && nums[stack.last().unwrap()] < nums[i] {
let idx = stack.pop().unwrap();
result[idx] = nums[i];
}
stack.push(i as i32);
}
println!("{:?}", result);Solution
Step 1: Identify type mismatch in stack usage
The stack stores indices to access nums. Indices are usize, but stack is Vec<i32> and i is cast to i32. This causes type mismatch and indexing errors.Step 2: Fix by using correct type for stack
Change stack to Vec<usize> and push i directly without casting. This matches indexing type and avoids errors.Final Answer:
Change stack type to Vec<usize> and remove i32 casts -> Option AQuick Check:
Stack indices must be usize = A [OK]
- Using i32 for indices causing type errors
- Pushing values instead of indices
- Replacing while with if losing multiple pops
[13, 7, 6, 12], apply the Next Greater Element using stack approach and provide the final output array.Solution
Step 1: Initialize stack and result
Start with empty stack and result = [-1, -1, -1, -1]. Process elements left to right.Step 2: Process each element and update result
- i=0 (13): stack empty, push 0 (stack=[0]) - i=1 (7): 13 > 7, push 1 (stack=[0,1]) - i=2 (6): 7 > 6, push 2 (stack=[0,1,2]) - i=3 (12): 6 < 12, pop 2, result[2]=12 7 < 12, pop 1, result[1]=12 13 > 12, push 3 (stack=[0,3])Step 3: Finalize result
Stack has indices 0 and 3 left, their result remains -1. Final result: [-1, 12, 12, -1]Final Answer:
[-1, 12, 12, -1] -> Option DQuick Check:
Next greater elements = [-1, 12, 12, -1] [OK]
- Assigning wrong next greater for first element
- Not popping all smaller elements before pushing
- Confusing values and indices in stack
Ready to prove it?
Pass the test and this pattern moves unknown
