githubEdit

10. Subarrays with First Element Minimum

βœ… GFG solution to the Subarrays with First Element Minimum problem: count subarrays where the first element is the minimum using monotonic stack technique. πŸš€

The problem can be found at the following link: πŸ”— Question Linkarrow-up-right

🧩 Problem Description

You are given an integer array arr[]. Your task is to count the number of subarrays where the first element is the minimum element of that subarray.

A subarray is valid if its first element is not greater than any other element in that subarray.

πŸ“˜ Examples

Example 1

Input: arr[] = [1, 2, 1]
Output: 5
Explanation:
All possible subarrays are:
[1], [1, 2], [1, 2, 1], [2], [2, 1], [1]
Valid subarrays are:
[1], [1, 2], [1, 2, 1], [2], [1] β†’ total 5

Example 2

Input: arr[] = [1, 3, 5, 2]
Output: 8
Explanation:
Valid subarrays are: [1], [1, 3], [1, 3, 5], [1, 3, 5, 2], [3], [3, 5], [5], [2] β†’ total 8

πŸ”’ Constraints

  • $1 \le \text{arr.size()} \le 5 \times 10^4$

  • $1 \le \text{arr}[i] \le 10^5$

βœ… My Approach

The optimal approach uses a Monotonic Stack traversed from right to left to efficiently determine for each index how far the subarray can extend while keeping the first element as the minimum:

Monotonic Stack (Right to Left)

  1. Key Observation:

    • For any index i, arr[i] remains the minimum of subarray arr[i..j] as long as no element in arr[i+1..j] is strictly less than arr[i].

    • So we need to find the next index to the right where a strictly smaller element appears β€” call it nxt.

    • The number of valid subarrays starting at i is exactly nxt - i.

  2. Traverse Right to Left:

    • Maintain an array-based monotonic stack that stores indices in increasing order of their values (from bottom to top).

    • For each index i (moving right to left), pop all stack entries whose corresponding values are β‰₯ arr[i] β€” those are no longer useful as "next smaller" boundaries.

    • The top of the stack now holds the index of the next element strictly smaller than arr[i].

  3. Count Contribution:

    • If the stack is empty, all indices from i to n-1 are valid, so add n - i.

    • Otherwise, add st[top] - i, where st[top] is the nearest smaller boundary.

  4. Push Current Index:

    • Push i onto the stack for future elements to use as their boundary.

  5. Accumulate and Return:

    • Sum all contributions and return the total count.

πŸ“ Time and Auxiliary Space Complexity

  • Expected Time Complexity: O(n), as each element is pushed onto and popped from the stack at most once, resulting in a total of at most 2n stack operations across the entire traversal.

  • Expected Auxiliary Space Complexity: O(n), as the stack can hold at most n indices in the worst case (e.g., a strictly decreasing array where no element is ever popped).

πŸ§‘β€πŸ’» Code (C++)

chevron-right⚑ View Alternative Approaches with Code and Analysishashtag

πŸ“Š 2️⃣ Standard Stack Approach

πŸ’‘ Algorithm Steps:

  1. Traverse array from right to left using a standard STL monotonic stack.

  2. For each index, pop elements from the stack while they are greater than or equal to the current element.

  3. The stack top gives the next strictly smaller element's index β€” this becomes the right boundary.

  4. Add (boundary - i) to the answer and push the current index.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(n) β€” Each element is pushed and popped at most once

  • Auxiliary Space: πŸ’Ύ O(n) β€” Stack can hold up to n elements

βœ… Why This Approach?

  • Most readable and standard implementation using STL.

  • Clear monotonic stack pattern with intuitive variable naming.

  • Easy to debug and verify correctness.

πŸ“Š 3️⃣ Next Smaller Precomputation

πŸ’‘ Algorithm Steps:

  1. Precompute the next strictly smaller element's index for each position using a monotonic stack in a separate pass.

  2. Store boundaries in a nxt[] array; default value is n (meaning no smaller element exists).

  3. In a second pass, sum up nxt[i] - i for all indices to get the final count.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(n) β€” Two independent linear passes through the array

  • Auxiliary Space: πŸ’Ύ O(n) β€” Extra nxt[] array plus the stack

βœ… Why This Approach?

  • Cleanly separates the precomputation and accumulation phases.

  • The nxt[] array is reusable if boundary information is needed later.

  • Easier to test boundary values individually.

πŸ“Š 4️⃣ Greedy Expansion Approach

πŸ’‘ Algorithm Steps:

  1. For each starting index i, greedily extend the subarray to the right.

  2. Continue extending as long as the next element is greater than or equal to arr[i] (keeping arr[i] the minimum).

  3. Count all valid subarrays that start at i as (j - i + 1) where j is the furthest valid index.

  4. No extra data structure is needed β€” direct comparison.

πŸ“ Complexity Analysis:

  • Time: ⏱️ O(nΒ²) worst case, O(n) for strictly decreasing sequences

  • Auxiliary Space: πŸ’Ύ O(1) β€” No extra space required

βœ… Why This Approach?

  • Simple and intuitive β€” no stack or extra data structure needed.

  • Great for understanding the problem from first principles.

  • Works efficiently in practice for random or nearly decreasing inputs.

⚠️ Note: This approach results in Time Limit Exceeded (TLE) for large inputs near the constraint boundary. (fails ~1010/1120 test cases due to time constraints).

πŸ†š πŸ” Comparison of Approaches

πŸš€ Approach

⏱️ Time Complexity

πŸ’Ύ Space Complexity

βœ… Pros

⚠️ Cons

🏷️ Array-Based Stack

🟒 O(n)

🟒 O(n)

πŸš€ Fastest, cache-friendly

πŸ”§ Manual stack management

πŸ” Standard Stack

🟒 O(n)

🟒 O(n)

πŸ“– Most readable

πŸ’Ύ STL overhead

πŸ“Š Precomputation

🟒 O(n)

🟑 O(n)

🎯 Clear separation of phases

πŸ’Ύ Extra array storage

πŸ”„ Greedy Expansion

🟑 O(n²) worst

🟒 O(1)

⭐ No extra space, simple logic

πŸ”§ Worst case quadratic, TLE risk

πŸ† Best Choice Recommendation

🎯 Scenario

πŸŽ–οΈ Recommended Approach

πŸ”₯ Performance Rating

πŸ… Competitive Programming

πŸ₯‡ Array-Based Stack

β˜…β˜…β˜…β˜…β˜…

πŸ“– Readability & Debugging

πŸ₯ˆ Standard Stack

β˜…β˜…β˜…β˜…β˜†

πŸ”§ Need Boundary Information

πŸ₯‰ Precomputation

β˜…β˜…β˜…β˜…β˜†

🎯 Space Critical / Small Inputs

πŸ… Greedy Expansion

β˜…β˜…β˜…β˜†β˜†

β˜• Code (Java)

🐍 Code (Python)

🧠 Contribution and Support

For discussions, questions, or doubts related to this solution, feel free to connect on LinkedIn: πŸ“¬ Any Questions?arrow-up-right. Let's make this learning journey more collaborative!

⭐ If you find this helpful, please give this repository a star! ⭐


πŸ“Visitor Count

Visitor counter

Last updated