feat(patterns): pattern taxonomy + is_optimal
This commit is contained in:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user