feat(patterns): pattern taxonomy + is_optimal

This commit is contained in:
2025-09-08 16:03:14 +01:00
parent 5b768f6a21
commit 13bab63618
28 changed files with 1434 additions and 26 deletions

View File

@@ -1,6 +1,8 @@
name: Monotonic Stack
slug: monotonic-stack
difficulty_level: 3
pattern_type: data_structure
display_order: 15
description: >
Maintain a stack where elements are always in sorted order (either increasing or
@@ -267,3 +269,245 @@ related_patterns:
- sliding-window
prerequisite_patterns: []
visualization_examples:
- id: largest-rectangle-histogram
title: "Largest Rectangle in Histogram"
input:
heights: [2, 1, 5, 6, 2, 3]
code: |
def largest_rectangle(heights):
stack = [] # (index, height)
max_area = 0
for i, h in enumerate(heights):
start = i
while stack and stack[-1][1] > h:
idx, height = stack.pop()
max_area = max(max_area, height * (i - idx))
start = idx
stack.append((start, h))
for idx, height in stack:
max_area = max(max_area, height * (len(heights) - idx))
return max_area
steps:
- id: step-1
description: "Goal: Find the largest rectangle that fits in this histogram. Each bar's height is shown, and we want to find the biggest rectangular area."
structures:
heights:
type: histogram
bars:
- { value: 2, state: default }
- { value: 1, state: default }
- { value: 5, state: default }
- { value: 6, state: default }
- { value: 2, state: default }
- { value: 3, state: default }
variables:
question: "What's the largest rectangle?"
max_area: 0
- id: step-2
description: "We scan left to right. Bar 0 (height 2) could be the shortest bar in some rectangle. Push it to stack. Format: h:height @start_index."
structures:
heights:
type: histogram
bars:
- { value: 2, state: active }
- { value: 1, state: default }
- { value: 5, state: default }
- { value: 6, state: default }
- { value: 2, state: default }
- { value: 3, state: default }
pointers:
i: 0
stack (h:height @start):
type: stack
values:
- { value: "h:2 @0", state: active }
variables:
max_area: 0
- id: step-3
description: "Bar 1 (height 1) is SHORTER than bar 0 (height 2). Bar 0's rectangle can't extend further right! Pop it and calculate: width=1, area=2x1=2."
structures:
heights:
type: histogram
bars:
- { value: 2, state: comparing }
- { value: 1, state: active }
- { value: 5, state: default }
- { value: 6, state: default }
- { value: 2, state: default }
- { value: 3, state: default }
pointers:
i: 1
rectangle:
startIndex: 0
endIndex: 0
height: 2
state: comparing
label: "Area: 2"
stack (h:height @start):
type: stack
values: []
variables:
max_area: 2
calculation: "height(2) × width(1) = 2"
- id: step-4
description: "Now push bar 1 (height 1). Since bar 0 was popped, bar 1's rectangle can extend back to index 0. Stack stores (0, 1)."
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: active }
- { value: 5, state: default }
- { value: 6, state: default }
- { value: 2, state: default }
- { value: 3, state: default }
pointers:
i: 1
maxArea: 2
stack (h:height @start):
type: stack
values:
- { value: "h:1 @0", state: active }
variables:
max_area: 2
insight: "Height 1 can extend back to index 0!"
- id: step-5
description: "Bar 2 (height 5) is taller than bar 1. Taller bars might extend further right, so push it. Bar 3 (height 6) is also taller — push it too."
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: default }
- { value: 5, state: active }
- { value: 6, state: active }
- { value: 2, state: default }
- { value: 3, state: default }
pointers:
i: 3
maxArea: 2
stack (h:height @start):
type: stack
values:
- { value: "h:6 @3", state: active }
- { value: "h:5 @2", state: active }
- { value: "h:1 @0", state: default }
variables:
max_area: 2
stack_note: "Stack keeps increasing heights (bottom to top)"
- id: step-6
description: "Bar 4 (height 2) is SHORTER than bar 3 (height 6). Pop bar 3: width=1, area=6x1=6. Still shorter than bar 2 (height 5), pop it: width=2, area=5x2=10!"
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: default }
- { value: 5, state: comparing }
- { value: 6, state: comparing }
- { value: 2, state: active }
- { value: 3, state: default }
pointers:
i: 4
rectangle:
startIndex: 2
endIndex: 3
height: 5
state: found
label: "Area: 10"
maxArea: 10
stack (h:height @start):
type: stack
values:
- { value: "h:1 @0", state: default }
variables:
max_area: 10
calculation: "height(5) × width(2) = 10"
- id: step-7
description: "Push bar 4 at index 2 (it extends back to where bar 2 was). Bar 5 (height 3) is taller, so push it too."
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: default }
- { value: 5, state: visited }
- { value: 6, state: visited }
- { value: 2, state: active }
- { value: 3, state: active }
pointers:
i: 5
maxArea: 10
stack (h:height @start):
type: stack
values:
- { value: "h:3 @5", state: active }
- { value: "h:2 @2", state: active }
- { value: "h:1 @0", state: default }
variables:
max_area: 10
note: "h:2 starts @2 because it extends back"
- id: step-8
description: "Done scanning! Process remaining stack: bar 5 (height 3) extends from index 5 to end (width 1), area=3. Bar 4 (height 2) extends from 2 to end (width 4), area=8."
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: visited }
- { value: 5, state: visited }
- { value: 6, state: visited }
- { value: 2, state: comparing }
- { value: 3, state: comparing }
rectangle:
startIndex: 2
endIndex: 5
height: 2
state: comparing
label: "Area: 8"
maxArea: 10
stack (h:height @start):
type: stack
values:
- { value: "h:1 @0", state: default }
variables:
max_area: 10
remaining: "Still processing stack..."
- id: step-9
description: "Bar 1 (height 1) extends from index 0 to end (width 6), area=6. The largest rectangle has area 10 — the one spanning bars 2-3 with height 5!"
structures:
heights:
type: histogram
bars:
- { value: 2, state: visited }
- { value: 1, state: visited }
- { value: 5, state: found }
- { value: 6, state: found }
- { value: 2, state: visited }
- { value: 3, state: visited }
rectangle:
startIndex: 2
endIndex: 3
height: 5
state: found
label: "Max: 10"
maxArea: 10
stack (h:height @start):
type: stack
values: []
variables:
max_area: 10
answer: "Largest rectangle = 10"