diff --git a/frontend/src/app/patterns/[slug]/page.tsx b/frontend/src/app/patterns/[slug]/page.tsx index c1b30ab..42b34b2 100644 --- a/frontend/src/app/patterns/[slug]/page.tsx +++ b/frontend/src/app/patterns/[slug]/page.tsx @@ -14,13 +14,14 @@ import { RelatedPatterns, } from "@/components/patterns"; import { PatternVisualization } from "@/components/visualization"; -import { TwoPointersVisualization, PrefixSumVisualization, LinkedListVisualization } from "@/components/visualizations-new"; +import { TwoPointersVisualization, PrefixSumVisualization, LinkedListVisualization, MonotonicStackVisualization } from "@/components/visualizations-new"; import { twoSumAlgorithm } from "@/content/algorithms/two-sum"; import { slidingWindowAlgorithm } from "@/content/algorithms/sliding-window"; import { binarySearchAlgorithm } from "@/content/algorithms/binary-search"; import { prefixSumAlgorithm } from "@/content/algorithms/prefix-sum"; import { fastSlowPointersAlgorithm } from "@/content/algorithms/fast-slow-pointers"; import { linkedListReversalAlgorithm } from "@/content/algorithms/linkedlist-reversal"; +import { monotonicStackAlgorithm } from "@/content/algorithms/monotonic-stack"; interface PageProps { params: Promise<{ slug: string }>; @@ -125,6 +126,8 @@ export default async function PatternDetailPage({ params }: PageProps) { ) : slug === "linkedlist-reversal" ? ( + ) : slug === "monotonic-stack" ? ( + ) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? ( diff --git a/frontend/src/components/visualizations-new/algorithms/monotonic-stack.tsx b/frontend/src/components/visualizations-new/algorithms/monotonic-stack.tsx new file mode 100644 index 0000000..6f0b7a1 --- /dev/null +++ b/frontend/src/components/visualizations-new/algorithms/monotonic-stack.tsx @@ -0,0 +1,81 @@ +'use client'; + +import { useVisualization } from '@/lib/visualizations/use-visualization'; +import type { AlgorithmDefinition } from '@/lib/visualizations/types'; +import { VisualizationContainer } from '../core/visualization-container'; +import { ArrayView } from '../data-structures/array-view'; +import { StackView } from '../data-structures/stack-view'; + +interface MonotonicStackVisualizationProps { + algorithm: AlgorithmDefinition; + className?: string; +} + +export function MonotonicStackVisualization({ + algorithm, + className, +}: MonotonicStackVisualizationProps) { + const { + currentStep, + currentStepIndex, + totalSteps, + playback, + controls, + currentPhase, + progress, + } = useVisualization(algorithm); + + const { dataState } = currentStep; + + // Input array with i pointer + const inputArray = dataState.arrays.find((a) => a.id === 'input'); + // Result array + const resultArray = dataState.arrays.find((a) => a.id === 'result'); + // Stack + const stack = dataState.stacks?.[0]; + + // Filter pointers for the input array (only show 'i' pointer on input) + const inputPointers = dataState.pointers.filter((p) => p.id === 'i'); + + return ( + + + {/* Input array with i pointer */} + {inputArray && ( + + )} + + + {/* Stack (left side) */} + {stack && } + + {/* Result array (right side) */} + {resultArray && ( + + )} + + + + ); +} diff --git a/frontend/src/components/visualizations-new/data-structures/stack-view.tsx b/frontend/src/components/visualizations-new/data-structures/stack-view.tsx new file mode 100644 index 0000000..cf93905 --- /dev/null +++ b/frontend/src/components/visualizations-new/data-structures/stack-view.tsx @@ -0,0 +1,48 @@ +'use client'; + +import { AnimatePresence } from 'framer-motion'; +import { cn } from '@/lib/utils'; +import type { StackState } from '@/lib/visualizations/types'; +import { StackElement } from '../primitives/stack-element'; + +interface StackViewProps { + stack: StackState; + className?: string; +} + +export function StackView({ stack, className }: StackViewProps) { + const hasElements = stack.elements.length > 0; + + return ( + + {stack.label && ( + + {stack.label} + + )} + + + {/* Empty state */} + {!hasElements && ( + + empty + + )} + + {/* Stack elements - flex-col-reverse makes first element appear at bottom */} + + {stack.elements.map((element, index) => ( + + ))} + + + + {/* Bottom indicator */} + + + ); +} diff --git a/frontend/src/components/visualizations-new/index.ts b/frontend/src/components/visualizations-new/index.ts index 192c549..6a89d41 100644 --- a/frontend/src/components/visualizations-new/index.ts +++ b/frontend/src/components/visualizations-new/index.ts @@ -11,12 +11,15 @@ export { Pointer } from "./primitives/pointer"; export { CalculationBubble } from "./primitives/calculation-bubble"; export { LinkedListNode } from "./primitives/linked-list-node"; export { LinkedListPointer } from "./primitives/linked-list-pointer"; +export { StackElement } from "./primitives/stack-element"; // Data structures export { ArrayView } from "./data-structures/array-view"; export { LinkedListView } from "./data-structures/linked-list-view"; +export { StackView } from "./data-structures/stack-view"; // Algorithm visualizations +export { MonotonicStackVisualization } from "./algorithms/monotonic-stack"; export { PrefixSumVisualization } from "./algorithms/prefix-sum"; export { TwoPointersVisualization } from "./algorithms/two-pointers"; export { LinkedListVisualization } from "./algorithms/linked-list"; diff --git a/frontend/src/components/visualizations-new/primitives/stack-element.tsx b/frontend/src/components/visualizations-new/primitives/stack-element.tsx new file mode 100644 index 0000000..c76225d --- /dev/null +++ b/frontend/src/components/visualizations-new/primitives/stack-element.tsx @@ -0,0 +1,54 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { cn } from '@/lib/utils'; +import type { StackElementState } from '@/lib/visualizations/types'; + +interface StackElementProps { + element: StackElementState; + isTop?: boolean; + className?: string; +} + +const STATE_CLASSES = { + normal: 'bg-[var(--surface-variant)] border-[var(--border)] text-[var(--foreground)]', + highlighted: 'bg-[var(--primary)]/20 border-[var(--primary)] text-[var(--primary)]', + popped: 'bg-[var(--error)]/20 border-[var(--error)] text-[var(--error)]', + pushing: 'bg-[var(--success)]/20 border-[var(--success)] text-[var(--success)]', +} as const; + +export function StackElement({ + element, + isTop = false, + className, +}: StackElementProps) { + return ( + + {element.value} + {isTop && ( + + TOP + + )} + + ); +} diff --git a/frontend/src/content/algorithms/monotonic-stack.ts b/frontend/src/content/algorithms/monotonic-stack.ts new file mode 100644 index 0000000..506d7fe --- /dev/null +++ b/frontend/src/content/algorithms/monotonic-stack.ts @@ -0,0 +1,881 @@ +import type { AlgorithmDefinition } from '@/lib/visualizations/types'; + +export const monotonicStackAlgorithm: AlgorithmDefinition = { + id: 'monotonic-stack-nge', + title: 'Next Greater Element', + slug: 'monotonic-stack', + pattern: { + name: 'Monotonic Stack', + description: + 'Use a stack to maintain elements in monotonic order, efficiently finding next greater/smaller elements.', + }, + problemStatement: + 'For each element in an array, find the next element to its right that is greater. If none exists, return -1.', + intuition: + 'The stack maintains indices of elements waiting for their "next greater". When we encounter a larger element, we resolve all smaller waiting elements by popping them.', + code: { + language: 'python', + code: `def next_greater_element(nums: list[int]) -> list[int]: + n = len(nums) + result = [-1] * n + stack = [] # stores indices + + for i in range(n): + # Pop all smaller elements - current is their answer + while stack and nums[i] > nums[stack[-1]]: + idx = stack.pop() + result[idx] = nums[i] + stack.append(i) + + return result`, + }, + initialExample: { + input: { nums: [4, 5, 2, 10, 8] }, + expected: [5, 10, 10, -1, -1], + }, + steps: [ + // Phase 1: Problem (2 steps) + { + id: 'problem-1', + phase: 'problem', + explanation: + 'Given array [4, 5, 2, 10, 8], for each element find the next greater element to its right. If none exists, use -1.', + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'dimmed' }, + { value: -1, index: 1, state: 'dimmed' }, + { value: -1, index: 2, state: 'dimmed' }, + { value: -1, index: 3, state: 'dimmed' }, + { value: -1, index: 4, state: 'dimmed' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [], + calculations: [], + }, + }, + { + id: 'problem-2', + phase: 'problem', + explanation: + 'A brute force approach checks every pair: for each element, scan right to find a larger one. This is O(n^2). Can we do better?', + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'highlighted' }, + { value: 5, index: 1, state: 'comparing' }, + { value: 2, index: 2, state: 'dimmed' }, + { value: 10, index: 3, state: 'dimmed' }, + { value: 8, index: 4, state: 'dimmed' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'dimmed' }, + { value: -1, index: 1, state: 'dimmed' }, + { value: -1, index: 2, state: 'dimmed' }, + { value: -1, index: 3, state: 'dimmed' }, + { value: -1, index: 4, state: 'dimmed' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [], + calculations: [], + }, + }, + + // Phase 2: Intuition (3 steps) + { + id: 'intuition-1', + phase: 'intuition', + explanation: + 'Key insight: When we see a large element, it becomes the "next greater" for ALL smaller elements we have seen that are still waiting.', + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'highlighted' }, + { value: 5, index: 1, state: 'highlighted' }, + { value: 2, index: 2, state: 'highlighted' }, + { value: 10, index: 3, state: 'success' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'dimmed' }, + { value: -1, index: 1, state: 'dimmed' }, + { value: -1, index: 2, state: 'dimmed' }, + { value: -1, index: 3, state: 'dimmed' }, + { value: -1, index: 4, state: 'dimmed' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [], + calculations: [], + }, + }, + { + id: 'intuition-2', + phase: 'intuition', + explanation: + 'We use a stack to track elements waiting for their answer. When we find a larger element, we pop and resolve all smaller waiting elements.', + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'dimmed' }, + { value: -1, index: 1, state: 'dimmed' }, + { value: -1, index: 2, state: 'dimmed' }, + { value: -1, index: 3, state: 'dimmed' }, + { value: -1, index: 4, state: 'dimmed' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's0', value: 0, state: 'normal' }, + { id: 's1', value: 1, state: 'normal' }, + { id: 's2', value: 2, state: 'highlighted' }, + ], + }, + ], + pointers: [], + variables: [], + calculations: [], + }, + }, + { + id: 'intuition-3', + phase: 'intuition', + explanation: + 'The stack maintains a monotonically DECREASING sequence - elements get smaller as we go up. This ensures we pop in the right order.', + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'highlighted' }, + { value: 5, index: 1, state: 'highlighted' }, + { value: 2, index: 2, state: 'highlighted' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'dimmed' }, + { value: -1, index: 1, state: 'dimmed' }, + { value: -1, index: 2, state: 'dimmed' }, + { value: -1, index: 3, state: 'dimmed' }, + { value: -1, index: 4, state: 'dimmed' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's0', value: 0, state: 'normal' }, + { id: 's1', value: 1, state: 'normal' }, + { id: 's2', value: 2, state: 'normal' }, + ], + }, + ], + pointers: [], + variables: [], + calculations: [], + }, + }, + + // Phase 3: Code walkthrough (4 steps) + { + id: 'code-1', + phase: 'code', + explanation: + 'Initialize result array with -1 (default when no greater element exists) and an empty stack to track indices.', + codeLine: 3, + codeHighlightLines: [2, 3, 4], + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'normal' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [{ id: 'n', name: 'n', value: 5 }], + calculations: [], + }, + }, + { + id: 'code-2', + phase: 'code', + explanation: + 'Iterate through each element. For each position i, we check if current element is greater than elements at stack indices.', + codeLine: 6, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'normal' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [{ id: 'n', name: 'n', value: 5 }], + calculations: [], + }, + }, + { + id: 'code-3', + phase: 'code', + explanation: + 'While stack is not empty AND current element > element at stack top: pop the index and set its result to current element.', + codeLine: 8, + codeHighlightLines: [8, 9, 10], + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'normal' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [{ id: 'n', name: 'n', value: 5 }], + calculations: [], + }, + }, + { + id: 'code-4', + phase: 'code', + explanation: + 'After resolving smaller elements, push current index onto stack. It will wait for its own "next greater" element.', + codeLine: 11, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'normal' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [{ id: 'n', name: 'n', value: 5 }], + calculations: [], + }, + }, + + // Phase 4: Execution (~10 steps) + { + id: 'exec-1', + phase: 'execution', + explanation: 'i=0: Stack is empty, so we just push index 0. Value 4 waits for its next greater element.', + codeLine: 11, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'highlighted' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: -1, index: 0, state: 'normal' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [{ id: 's0', value: 0, state: 'pushing' }], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 0, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 0 }, + { id: 'current', name: 'nums[i]', value: 4 }, + ], + calculations: [], + }, + }, + { + id: 'exec-2', + phase: 'execution', + explanation: 'i=1: Current value 5 > nums[stack top] = nums[0] = 4. Pop index 0 and set result[0] = 5.', + codeLine: 9, + decision: { + question: 'Is 5 > nums[0]?', + answer: '5 > 4 = true', + action: 'Pop index 0, set result[0] = 5', + }, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'comparing' }, + { value: 5, index: 1, state: 'highlighted' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [{ id: 's0', value: 0, state: 'popped' }], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 1, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 1 }, + { id: 'current', name: 'nums[i]', value: 5 }, + { id: 'idx', name: 'popped idx', value: 0 }, + ], + calculations: [ + { id: 'calc-1', expression: 'result[0]', result: '5', position: 'above' }, + ], + }, + }, + { + id: 'exec-3', + phase: 'execution', + explanation: 'Stack is now empty. Push index 1. Value 5 waits for its next greater element.', + codeLine: 11, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'highlighted' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [{ id: 's1', value: 1, state: 'pushing' }], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 1, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 1 }, + { id: 'current', name: 'nums[i]', value: 5 }, + ], + calculations: [], + }, + }, + { + id: 'exec-4', + phase: 'execution', + explanation: 'i=2: Current value 2 < nums[stack top] = nums[1] = 5. No pop needed, just push index 2.', + codeLine: 11, + decision: { + question: 'Is 2 > nums[1]?', + answer: '2 > 5 = false', + action: 'Skip while loop, push index 2', + }, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'highlighted' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: -1, index: 1, state: 'normal' }, + { value: -1, index: 2, state: 'normal' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's1', value: 1, state: 'normal' }, + { id: 's2', value: 2, state: 'pushing' }, + ], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 2, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 2 }, + { id: 'current', name: 'nums[i]', value: 2 }, + ], + calculations: [], + }, + }, + { + id: 'exec-5', + phase: 'execution', + explanation: 'i=3: Current value 10 > nums[stack top] = nums[2] = 2. Pop index 2, set result[2] = 10.', + codeLine: 9, + decision: { + question: 'Is 10 > nums[2]?', + answer: '10 > 2 = true', + action: 'Pop index 2, set result[2] = 10', + }, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'comparing' }, + { value: 10, index: 3, state: 'highlighted' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: -1, index: 1, state: 'normal' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's1', value: 1, state: 'normal' }, + { id: 's2', value: 2, state: 'popped' }, + ], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 3, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 3 }, + { id: 'current', name: 'nums[i]', value: 10 }, + { id: 'idx', name: 'popped idx', value: 2 }, + ], + calculations: [ + { id: 'calc-2', expression: 'result[2]', result: '10', position: 'above' }, + ], + }, + }, + { + id: 'exec-6', + phase: 'execution', + explanation: 'Stack top is now index 1. Current 10 > nums[1] = 5. Pop index 1, set result[1] = 10.', + codeLine: 9, + decision: { + question: 'Is 10 > nums[1]?', + answer: '10 > 5 = true', + action: 'Pop index 1, set result[1] = 10', + }, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'comparing' }, + { value: 2, index: 2, state: 'dimmed' }, + { value: 10, index: 3, state: 'highlighted' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: 10, index: 1, state: 'success' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [{ id: 's1', value: 1, state: 'popped' }], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 3, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 3 }, + { id: 'current', name: 'nums[i]', value: 10 }, + { id: 'idx', name: 'popped idx', value: 1 }, + ], + calculations: [ + { id: 'calc-3', expression: 'result[1]', result: '10', position: 'above' }, + ], + }, + }, + { + id: 'exec-7', + phase: 'execution', + explanation: 'Stack is now empty. Push index 3. Value 10 waits for its next greater element.', + codeLine: 11, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'dimmed' }, + { value: 2, index: 2, state: 'dimmed' }, + { value: 10, index: 3, state: 'highlighted' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: 10, index: 1, state: 'success' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [{ id: 's3', value: 3, state: 'pushing' }], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 3, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 3 }, + { id: 'current', name: 'nums[i]', value: 10 }, + ], + calculations: [], + }, + }, + { + id: 'exec-8', + phase: 'execution', + explanation: 'i=4: Current value 8 < nums[stack top] = nums[3] = 10. No pop needed, just push index 4.', + codeLine: 11, + decision: { + question: 'Is 8 > nums[3]?', + answer: '8 > 10 = false', + action: 'Skip while loop, push index 4', + }, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'dimmed' }, + { value: 2, index: 2, state: 'dimmed' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'highlighted' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: 10, index: 1, state: 'success' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'normal' }, + { value: -1, index: 4, state: 'normal' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's3', value: 3, state: 'normal' }, + { id: 's4', value: 4, state: 'pushing' }, + ], + }, + ], + pointers: [{ id: 'i', name: 'i', index: 4, color: 'default', showValue: true }], + variables: [ + { id: 'i', name: 'i', value: 4 }, + { id: 'current', name: 'nums[i]', value: 8 }, + ], + calculations: [], + }, + }, + { + id: 'exec-9', + phase: 'execution', + explanation: 'Loop complete. Elements remaining in stack (indices 3, 4) have no next greater element - their result stays -1.', + codeLine: 13, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'dimmed' }, + { value: 5, index: 1, state: 'dimmed' }, + { value: 2, index: 2, state: 'dimmed' }, + { value: 10, index: 3, state: 'highlighted' }, + { value: 8, index: 4, state: 'highlighted' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: 10, index: 1, state: 'success' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'comparing' }, + { value: -1, index: 4, state: 'comparing' }, + ], + }, + ], + stacks: [ + { + id: 'stack', + label: 'Stack', + elements: [ + { id: 's3', value: 3, state: 'normal' }, + { id: 's4', value: 4, state: 'normal' }, + ], + }, + ], + pointers: [], + variables: [], + calculations: [], + }, + }, + { + id: 'exec-10', + phase: 'execution', + explanation: 'Final result: [5, 10, 10, -1, -1]. Each element found its next greater, or -1 if none exists. Time: O(n), Space: O(n).', + codeLine: 13, + dataState: { + arrays: [ + { + id: 'input', + label: 'Input Array', + elements: [ + { value: 4, index: 0, state: 'normal' }, + { value: 5, index: 1, state: 'normal' }, + { value: 2, index: 2, state: 'normal' }, + { value: 10, index: 3, state: 'normal' }, + { value: 8, index: 4, state: 'normal' }, + ], + }, + { + id: 'result', + label: 'Result', + elements: [ + { value: 5, index: 0, state: 'success' }, + { value: 10, index: 1, state: 'success' }, + { value: 10, index: 2, state: 'success' }, + { value: -1, index: 3, state: 'success' }, + { value: -1, index: 4, state: 'success' }, + ], + }, + ], + stacks: [{ id: 'stack', label: 'Stack', elements: [] }], + pointers: [], + variables: [ + { id: 'result', name: 'result', value: '[5, 10, 10, -1, -1]' }, + ], + calculations: [], + }, + }, + ], +};