π§ PHP Stack & Queue Patterns
Pattern 1 β Monotonic Stack (Next Greater Element)
A monotonic stack maintains elements in strictly increasing or decreasing order. Pop whenever the invariant is violated β that's the answer moment.
π§ How to Approach This
Step 1 β Recognize the pattern: "Next greater / smaller element" questions. Keywords: daily temperatures, next warmer day, stock span.
Step 2 β Brute force (what NOT to do): For each element scan right for next greater β O(nΒ²).
Step 3 β Optimal insight: Monotonic decreasing stack. When a larger element arrives, it's the "next greater" for everything it pops.
Step 4 β Algorithm:
1. Stack stores indices of unresolved elements
2. For each i: while stack not empty and arr[stack.top] < arr[i] β answer[pop] = arr[i]
3. Push i
Step 5 β Edge cases: Elements with no greater successor β remain in stack (answer stays -1).
function nextGreaterElement(array $nums): array {
$n = count($nums);
$result = array_fill(0, $n, -1);
$stack = []; // indices
for ($i = 0; $i < $n; $i++) {
while (!empty($stack) && $nums[end($stack)] < $nums[$i]) {
$result[array_pop($stack)] = $nums[$i];
}
$stack[] = $i;
}
return $result;
}
// [2,1,3,4] β [3,3,4,-1]
Time: O(n) β each element pushed/popped once | Space: O(n)
Pattern 2 β Monotonic Stack (Daily Temperatures)
Same pattern but answer = distance, not value.
π§ How to Approach This
Step 1 β Recognize the pattern: "How many days until a warmer temperature?"
Step 2 β Brute force (what NOT to do): Nested loops β O(nΒ²).
Step 3 β Optimal insight: Monotonic decreasing stack of indices. When warmer temp found, answer = i - popped_index.
Step 4 β Algorithm:
1. Stack of indices
2. For each i: while top's temp < current temp β result[pop] = i - pop
3. Push i
Step 5 β Edge cases: Last element β never warmer β 0.
function dailyTemperatures(array $temps): array {
$n = count($temps);
$result = array_fill(0, $n, 0);
$stack = [];
for ($i = 0; $i < $n; $i++) {
while (!empty($stack) && $temps[end($stack)] < $temps[$i]) {
$idx = array_pop($stack);
$result[$idx] = $i - $idx;
}
$stack[] = $i;
}
return $result;
}
Time: O(n) | Space: O(n)
Pattern 3 β Sliding Window Maximum (Deque / Monotonic Queue)
π§ How to Approach This
Step 1 β Recognize the pattern: "Maximum in sliding window of size k" β window moves right.
Step 2 β Brute force (what NOT to do): Scan k elements for each window β O(nΒ·k).
Step 3 β Optimal insight: Monotonic decreasing deque of indices. Front always holds current window's max index.
Step 4 β Algorithm:
1. For each i: remove indices outside window from front
2. Remove smaller elements from back (they can never be max)
3. Append result when window is full (i >= k-1)
Step 5 β Edge cases: k > n; k = 1.
function maxSlidingWindow(array $nums, int $k): array {
$n = count($nums);
$result = [];
$deque = []; // indices, monotonic decreasing
for ($i = 0; $i < $n; $i++) {
// Remove out-of-window index from front
while (!empty($deque) && $deque[0] < $i - $k + 1) {
array_shift($deque);
}
// Remove smaller elements from back
while (!empty($deque) && $nums[end($deque)] < $nums[$i]) {
array_pop($deque);
}
$deque[] = $i;
if ($i >= $k - 1) $result[] = $nums[$deque[0]];
}
return $result;
}
Time: O(n) | Space: O(k)
Pattern 4 β Implement Queue Using Two Stacks
π§ How to Approach This
Step 1 β Recognize the pattern: Two LIFO structures simulate one FIFO.
Step 2 β Brute force (what NOT to do): Single stack + reverse on every dequeue β expensive.
Step 3 β Optimal insight: Lazy transfer β move all elements to outStack only when outStack is empty.
Step 4 β Algorithm:
1. Push always goes to inStack
2. Pop/peek: if outStack empty, drain inStack into outStack first
Step 5 β Edge cases: Mixed push/pop operations, amortized O(1).
class MyQueue {
private array $inStack = [];
private array $outStack = [];
public function push(int $x): void {
$this->inStack[] = $x;
}
public function pop(): int {
$this->transfer();
return array_pop($this->outStack);
}
public function peek(): int {
$this->transfer();
return end($this->outStack);
}
public function empty(): bool {
return empty($this->inStack) && empty($this->outStack);
}
private function transfer(): void {
if (empty($this->outStack)) {
while (!empty($this->inStack)) {
$this->outStack[] = array_pop($this->inStack);
}
}
}
}
Time: O(1) amortized | Space: O(n)
Pattern 5 β Largest Rectangle in Histogram
π§ How to Approach This
Step 1 β Recognize the pattern: For each bar, find how far left and right it can extend at its height.
Step 2 β Brute force (what NOT to do): For each pair of bars compute area β O(nΒ²).
Step 3 β Optimal insight: Monotonic increasing stack. Pop when a shorter bar arrives β the popped bar defines a rectangle bounded by the shorter bar and the new stack top.
Step 4 β Algorithm:
1. Append sentinel 0 to heights
2. Monotonic increasing stack of indices
3. On pop: width = i - stack_top - 1; area = height * width
Step 5 β Edge cases: Sentinel at end forces all remaining bars to be processed.
function largestRectangleArea(array $heights): int {
$heights[] = 0; // sentinel
$stack = [-1];
$maxArea = 0;
foreach ($heights as $i => $h) {
while (count($stack) > 1 && $heights[end($stack)] > $h) {
$height = $heights[array_pop($stack)];
$width = $i - end($stack) - 1;
$maxArea = max($maxArea, $height * $width);
}
$stack[] = $i;
}
return $maxArea;
}
Time: O(n) | Space: O(n)