GFG QUESTION LINK
LEETCODE
π Problem Statement
Given a series of daily stock prices, we need to find the span of stock price for each day. The span for a day i is defined as the maximum number of consecutive days before (and including) day i for which the stock price is less than or equal to its price on day i.
Example:
Input: [100, 80, 60, 70, 60, 75, 85]
Output: [1, 1, 1, 2, 1, 4, 6]
π 1st Attempt: Stack-Based Approach (Inefficient)
π‘ Idea
I initially thought of using a stack to store previous prices, but instead of directly using it, I made a copy of the stack and iterated over it to count the span.
β What went wrong?
- Copying the stack every time added unnecessary complexity
- It led to O(nΒ²) complexity and caused TLE (Time Limit Exceeded) on 6 test cases
π» Code
class Solution {
public:
vector<int> calculateSpan(vector<int>& arr) {
stack<int> st;
int n = arr.size();
vector<int> ans(n);
for (int i = 0; i < n; i++) {
int count = 1;
stack<int> proxy = st; // Copying stack β
while (!proxy.empty() && proxy.top() < arr[i]) {
proxy.pop();
count++;
}
ans[i] = count;
st.push(arr[i]);
}
return ans;
}
};
π¨ Issue:
Copying the stack each time resulted in an additional O(n) operation inside a loop, making it O(nΒ²) overall.
π 2nd Attempt: Two-Pointer Approach (Better, but still O(nΒ²))
π‘ Idea
I removed the stack and instead used a backward pointer (j) to find the span for each element.
β What went wrong?
- Still O(nΒ²) in the worst case (when elements are in increasing order).
- Failed 1 test case due to TLE (1115/1116 passed).
π» Code
class Solution {
public:
vector<int> calculateSpan(vector<int>& arr) {
int n = arr.size();
vector<int> ans;
for (int i = 0; i < n; i++) {
int count = 1;
int j = i;
while (j > 0 && arr[j - 1] <= arr[i]) { // Checking all previous elements β
j--;
count++;
}
ans.push_back(count);
}
return ans;
}
};
π¨ Issue:
Although better than the first approach, the worst-case scenario (increasing order of prices) still made it O(nΒ²).
β Final Attempt: Optimized Stack-Based Approach (O(n) Time Complexity)
π‘ Key Insight
Instead of storing only prices, I stored both the price and the count as {price, count}
pairs in the stack.
β¨ Why This Works?
- Instead of iterating over previous elements, we store their spans directly in the stack.
- When popping elements, we directly add their span count to the current span instead of rechecking them.
- This ensures O(n) complexity as each element is processed only once.
π₯ Final Optimized Code
class Solution {
public:
vector<int> calculateSpan(vector<int>& arr) {
stack<pair<int, int>> st;
int n = arr.size();
vector<int> ans(n);
for (int i = 0; i < n; i++) {
int count = 1;
while (!st.empty() && st.top().first <= arr[i]) {
count += st.top().second; // Directly add stored counts β
st.pop();
}
st.push({arr[i], count});
ans[i] = count;
}
return ans;
}
};
π Time Complexity: O(n)
Each element is processed only once, making this approach highly efficient.
π₯ Key Learnings & Takeaways
1οΈβ£ Avoid redundant operations β Copying the stack (or unnecessary iterations) adds overhead.
2οΈβ£ Store useful data smartly β Instead of recalculating spans, storing counts in the stack saved time.
3οΈβ£ Use data structures efficiently β Leveraging a monotonic stack made the solution significantly faster.
β¨ Conclusion
The journey from O(nΒ²) to O(n) was all about optimizing how we track previous values. Using a stack efficiently made all the difference!
π‘ What do you think about this approach? Have you solved this problem differently? Letβs discuss in the comments! π
Top comments (0)