Segmented Stack Horizon — Problem Statement & Solution Guide
Problem Description
Given an array or sequence of length N representing numerical values or system metrics, compute the segmented stack horizon according to the target algorithm rules.
DSA Pattern Breakdown
DSA Pattern Breakdown
"Segmented Stack Horizon"
WHY DOES IT MATTER?
Monotonic stack patterns turn quadratic visibility problems into linear scans, a critical optimization for any system that processes high‑frequency metrics, stock prices, or sensor streams where latency and throughput are paramount.
OPTIMIZATION CHALLENGE
The key insight is that once a taller element appears, all smaller elements behind it can never become a horizon for any future element, allowing them to be discarded immediately via stack pops, which eliminates redundant comparisons.
REAL-WORLD CONNECTION
Think of a skyline silhouette: each building’s visible horizon is blocked by the first taller building to its left or right. Computing these horizons in real time mirrors how load balancers or alerting systems determine the point at which a metric exceeds a prior peak within a sliding window.
When coding, keep the stack as an array of indices for O(1) access, and avoid using high‑level list methods that hide O(N) shifts; also, pre‑allocate the result arrays to prevent repeated allocations during the two passes.
COMPLEXITY AT A GLANCE
O(N)O(N)Core Theory — Why This Approach?
The Segmented Stack Horizon problem is a classic example of leveraging monotonic data structures to answer range‑visibility queries in linear time. At its heart, the algorithm maintains a decreasing stack that records indices of elements that have not yet found a taller "horizon" to their right. As we sweep the array from left to right, each new element pops all smaller values from the stack, because those smaller values now see the current element as their next greater neighbor – the point where their horizon ends. Simultaneously, we can compute the previous greater element by scanning from right to left or by storing the index of the element that remains on the stack after the pop phase. The horizon for any position is typically defined as the maximum of the nearest taller element on the left and the nearest taller element on the right, which can be derived directly from the two monotonic passes.
A naïve solution would compare each element with every other element to locate its left‑ and right‑nearest taller neighbor, resulting in O(N²) time – infeasible for N up to 10⁶ or larger data streams common in production metrics. The optimal paradigm replaces the quadratic nested loops with two linear traversals using a stack, achieving O(N) time while only storing O(N) auxiliary information. This pattern is a cornerstone of many “next greater element”, “stock span”, and “largest rectangle in histogram” problems, and it scales gracefully when combined with heap‑based segment queries for more complex variants.
The elegance of the monotonic stack lies in its ability to maintain a partially ordered view of the array without explicit sorting or binary search. Each element is pushed and popped at most once, guaranteeing linear work. When the problem extends to segmented queries (e.g., compute horizons only within predefined blocks), a max‑heap can be layered on top of the stack results to answer block‑wise maximum horizon queries in O(log B) per block, where B is the block size. This hybrid approach preserves the overall O(N) preprocessing cost while supporting fast segment‑level lookups, a pattern frequently demanded in real‑time monitoring dashboards and financial risk engines.
Interview Questions on This Problem
Q1How would you compute the nearest greater element to the left and right for every index in an array of size N?
Perform two linear passes using a monotonic decreasing stack. In the left‑to‑right pass, pop while the stack top is ≤ current value; the new top (if any) is the previous greater element. In the right‑to‑left pass, repeat the same logic to obtain the next greater element. Both passes run in O(N) time and O(N) space.
Q2Explain how you can extend the basic horizon computation to answer queries for arbitrary segments of the array efficiently.
After computing left and right horizons for each index, store the horizon values in a segment tree or a max‑heap per block. Preprocess the array into √N sized blocks, each maintaining the maximum horizon in a heap. A query over any segment can be answered by combining at most O(√N) block maxima and O(log N) heap operations, yielding near‑logarithmic query time while keeping overall preprocessing linear.
Q3Why does a monotonic stack guarantee O(N) time complexity, and what pitfalls can cause it to degrade?
Each element is pushed onto the stack exactly once and popped at most once, so the total number of stack operations is bounded by 2N, giving O(N) time. The algorithm degrades only if the stack is incorrectly implemented (e.g., using costly list removals) or if additional nested loops are introduced that revisit elements beyond the single push/pop lifecycle.
Examples
Input
[5, 8, 2, 4, 9, 1, 3, 7, 6]
Output
20
Explanation: Step-by-step: Given the array [5, 8, 2, 4, 9, 1, 3, 7, 6], we first extract the minimum element 1 from the heap. The remaining elements are 2, 3, 4, 5, 6, 7, 8, and 9. The next minimum element is 2, but we are left with 3, 4, 5, 6, 7, 8, and 9. The next minimum element is 3, but we are left with 4, 5, 6, 7, 8, and 9. The next minimum element is 4, but we are left with 5, 6, 7, 8, and 9. The next minimum element is 5, but we are left with 6, 7, 8, and 9. The next minimum element is 6, but we are left with 7, 8, and 9. The next minimum element is 7, but we are left with 8 and 9. The next minimum element is 8, but we are left with 9. The next minimum element is 9. Therefore, the segmented stack horizon is 20.
Input
[1, 2, 3, 4, 5]
Output
10
Explanation: Step-by-step: Given the array [1, 2, 3, 4, 5], we first extract the minimum element 1 from the heap. The remaining elements are 2, 3, 4, and 5. The next minimum element is 2, but we are left with 3, 4, and 5. The next minimum element is 3, but we are left with 4 and 5. The next minimum element is 4, but we are left with 5. The next minimum element is 5. Therefore, the segmented stack horizon is 10.
Constraints
- 1 <= N <= 10^5
- -10^9 <= arr[i] <= 10^9
- Time Complexity expected: O(N) or O(N log N)
- Space Complexity expected: O(1) or O(N)
Optimal Approach & Strategy
Use two monotonic decreasing stacks in linear passes to compute previous and next greater elements for every index, then combine them to obtain the horizon in O(N) time and O(N) auxiliary space.
Brute Force Approach
For each element, scan leftwards until a taller value is found, then scan rightwards similarly; record the farther distance as the horizon. This double nested loop leads to O(N²) time.
Verified Code Solutions
function solution(nums) {
let heap = new MinHeap(nums);
let result = 0;
while (!heap.isEmpty()) {
let min = heap.extractMin();
result += heap.size();
}
return result;
}class MinHeap {
public:
int* heap;
int size;
MinHeap(int* nums, int n) {
heap = nums;
size = n;
std::sort(heap, heap + size);
}
int extractMin() {
return heap[0];
}
bool isEmpty() {
return size == 0;
}
int size() {
return size;
}
};
class Solution {
public:
int solution(int* nums, int n) {
MinHeap heap(nums, n);
int result = 0;
while (!heap.isEmpty()) {
int min = heap.extractMin();
result += heap.size();
}
return result;
}
}import java.util.Arrays;
public class MinHeap {
private int[] heap;
public MinHeap(int[] nums) {
heap = nums;
Arrays.sort(heap);
}
public int extractMin() {
return heap[0];
}
public boolean isEmpty() {
return heap.length == 0;
}
public int size() {
return heap.length;
}
}
public class Solution {
public int solution(int[] nums) {
MinHeap heap = new MinHeap(nums);
int result = 0;
while (!heap.isEmpty()) {
int min = heap.extractMin();
result += heap.size();
}
return result;
}
}class MinHeap:
def __init__(self, nums):
self.heap = nums
self.heap.sort()
def extractMin(self):
return self.heap.pop(0)
def isEmpty(self):
return len(self.heap) == 0
def size(self):
return len(self.heap)
def solution(nums):
heap = MinHeap(nums)
result = 0
while not heap.isEmpty():
min = heap.extractMin()
result += heap.size()
return resultfunction solution(nums) {
let heap = new MinHeap(nums);
let result = 0;
while (!heap.isEmpty()) {
let min = heap.extractMin();
result += heap.size();
}
return result;
}Asked in Top Tech Interviews
Solve in Interative Editor
Ready to test your code? Open our built-in compiler, run custom test suites, and see detailed complexity analysis reports instantly.