diff --git a/frontend/src/components/visualizations-new/data-structures/array-view.tsx b/frontend/src/components/visualizations-new/data-structures/array-view.tsx
index 6432692..41f9fd3 100644
--- a/frontend/src/components/visualizations-new/data-structures/array-view.tsx
+++ b/frontend/src/components/visualizations-new/data-structures/array-view.tsx
@@ -39,6 +39,30 @@ export function ArrayView({
// Calculate total array width for proper pointer alignment
const totalWidth = array.elements.length * elementWidth + (array.elements.length - 1) * gap;
+ // Check if any pointers are too close together (within 1 index) - hide values to save space
+ const hasClosePointers = pointers.length > 1 && pointers.some((p, i) => {
+ return pointers.some((other, j) => i !== j && Math.abs(p.index - other.index) <= 1);
+ });
+
+ // Group pointers by index to calculate offsets for overlapping pointers at same position
+ const pointersByIndex = pointers.reduce((acc, pointer) => {
+ const idx = pointer.index;
+ if (!acc[idx]) acc[idx] = [];
+ acc[idx].push(pointer);
+ return acc;
+ }, {} as Record
);
+
+ // Calculate horizontal offset for pointers at the same index
+ const getPointerOffset = (pointer: PointerState): number => {
+ const pointersAtIndex = pointersByIndex[pointer.index] || [];
+ if (pointersAtIndex.length <= 1) return 0;
+
+ const pointerIdx = pointersAtIndex.findIndex(p => p.id === pointer.id);
+ const totalPointers = pointersAtIndex.length;
+ const spacing = 36; // px between overlapping pointers
+ return (pointerIdx - (totalPointers - 1) / 2) * spacing;
+ };
+
return (
{array.label && (
@@ -58,6 +82,8 @@ export function ArrayView({
elementWidth={elementWidth}
gap={gap}
value={array.elements[pointer.index]?.value}
+ hideValue={hasClosePointers}
+ horizontalOffset={getPointerOffset(pointer)}
/>
))}
diff --git a/frontend/src/components/visualizations-new/data-structures/linked-list-view.tsx b/frontend/src/components/visualizations-new/data-structures/linked-list-view.tsx
new file mode 100644
index 0000000..c198d63
--- /dev/null
+++ b/frontend/src/components/visualizations-new/data-structures/linked-list-view.tsx
@@ -0,0 +1,80 @@
+'use client';
+
+import { cn } from '@/lib/utils';
+import type { LinkedListState, LinkedListPointerState } from '@/lib/visualizations/types';
+import { LinkedListNode } from '../primitives/linked-list-node';
+import { LinkedListPointer } from '../primitives/linked-list-pointer';
+
+interface LinkedListViewProps {
+ list: LinkedListState;
+ pointers: LinkedListPointerState[];
+ className?: string;
+}
+
+const NODE_WIDTH = 40; // w-10 = 40px
+const ARROW_WIDTH = 22; // 16px line + 6px arrow head
+
+export function LinkedListView({
+ list,
+ pointers,
+ className,
+}: LinkedListViewProps) {
+ // Calculate total width for proper pointer alignment
+ const totalWidth = list.nodes.length * NODE_WIDTH + (list.nodes.length - 1) * ARROW_WIDTH;
+
+ // Group pointers by nodeIndex to calculate offsets for overlapping pointers
+ const pointersByIndex = pointers.reduce((acc, pointer) => {
+ const idx = pointer.nodeIndex;
+ if (!acc[idx]) acc[idx] = [];
+ acc[idx].push(pointer);
+ return acc;
+ }, {} as Record);
+
+ // Calculate horizontal offset for each pointer
+ const getPointerOffset = (pointer: LinkedListPointerState): number => {
+ const pointersAtIndex = pointersByIndex[pointer.nodeIndex] || [];
+ if (pointersAtIndex.length <= 1) return 0;
+
+ const pointerIdx = pointersAtIndex.findIndex(p => p.id === pointer.id);
+ const totalPointers = pointersAtIndex.length;
+ const spacing = 28; // px between overlapping pointers
+ // Center the group: offset from center based on position
+ return (pointerIdx - (totalPointers - 1) / 2) * spacing;
+ };
+
+ return (
+
+ {list.label && (
+
+ {list.label}
+
+ )}
+
+
+ {/* Pointers row */}
+
+ {pointers.map((pointer) => (
+
+ ))}
+
+
+ {/* Linked list nodes */}
+
+ {list.nodes.map((node, index) => (
+
+ ))}
+
+
+
+ );
+}
diff --git a/frontend/src/components/visualizations-new/index.ts b/frontend/src/components/visualizations-new/index.ts
index 25831ab..192c549 100644
--- a/frontend/src/components/visualizations-new/index.ts
+++ b/frontend/src/components/visualizations-new/index.ts
@@ -9,10 +9,14 @@ export { VariableInspector } from "./core/variable-inspector";
export { ArrayElement } from "./primitives/array-element";
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";
// Data structures
export { ArrayView } from "./data-structures/array-view";
+export { LinkedListView } from "./data-structures/linked-list-view";
// Algorithm visualizations
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/linked-list-node.tsx b/frontend/src/components/visualizations-new/primitives/linked-list-node.tsx
new file mode 100644
index 0000000..4a46a74
--- /dev/null
+++ b/frontend/src/components/visualizations-new/primitives/linked-list-node.tsx
@@ -0,0 +1,64 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import { cn } from '@/lib/utils';
+import type { LinkedListNodeState } from '@/lib/visualizations/types';
+
+interface LinkedListNodeProps {
+ node: LinkedListNodeState;
+ isLast?: boolean;
+ className?: string;
+}
+
+const STATE_CLASSES = {
+ normal: 'bg-surface-variant border-border text-foreground',
+ highlighted: 'bg-primary/20 border-primary text-primary',
+ dimmed: 'bg-surface-variant/50 border-border/50 text-foreground-muted opacity-40',
+ success: 'bg-viz-success/20 border-viz-success text-viz-success',
+ comparing: 'bg-viz-compare/20 border-viz-compare text-viz-compare',
+} as const;
+
+export function LinkedListNode({
+ node,
+ isLast = false,
+ className,
+}: LinkedListNodeProps) {
+ if (node.isNull) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+ {node.value}
+
+ {!isLast && (
+
+
+
+
+ )}
+
+ );
+}
diff --git a/frontend/src/components/visualizations-new/primitives/linked-list-pointer.tsx b/frontend/src/components/visualizations-new/primitives/linked-list-pointer.tsx
new file mode 100644
index 0000000..791d5d9
--- /dev/null
+++ b/frontend/src/components/visualizations-new/primitives/linked-list-pointer.tsx
@@ -0,0 +1,59 @@
+'use client';
+
+import { motion } from 'framer-motion';
+import { cn } from '@/lib/utils';
+import type { LinkedListPointerState } from '@/lib/visualizations/types';
+
+interface LinkedListPointerProps {
+ pointer: LinkedListPointerState;
+ nodeWidth: number;
+ arrowWidth: number;
+ /** Horizontal offset when multiple pointers at same position */
+ horizontalOffset?: number;
+}
+
+const COLOR_CLASSES = {
+ slow: 'text-viz-pointer-left',
+ fast: 'text-viz-pointer-right',
+ prev: 'text-viz-pointer-mid',
+ curr: 'text-viz-pointer-left',
+ next: 'text-viz-pointer-right',
+ default: 'text-viz-pointer-default',
+} as const;
+
+export function LinkedListPointer({
+ pointer,
+ nodeWidth,
+ arrowWidth,
+ horizontalOffset = 0,
+}: LinkedListPointerProps) {
+ // Calculate x position based on node index, with offset for overlapping pointers
+ const x = pointer.nodeIndex * (nodeWidth + arrowWidth) + nodeWidth / 2 + horizontalOffset;
+
+ return (
+
+ {pointer.name}
+
+
+ );
+}
diff --git a/frontend/src/components/visualizations-new/primitives/pointer.tsx b/frontend/src/components/visualizations-new/primitives/pointer.tsx
index 3a8d5bb..6daa200 100644
--- a/frontend/src/components/visualizations-new/primitives/pointer.tsx
+++ b/frontend/src/components/visualizations-new/primitives/pointer.tsx
@@ -9,6 +9,10 @@ interface PointerProps {
elementWidth: number;
gap: number;
value?: number;
+ /** Hide the value to save space when pointers are close */
+ hideValue?: boolean;
+ /** Horizontal offset when multiple pointers at same position */
+ horizontalOffset?: number;
className?: string;
}
@@ -24,10 +28,12 @@ export function Pointer({
elementWidth,
gap,
value,
+ hideValue = false,
+ horizontalOffset = 0,
className,
}: PointerProps) {
- // Calculate center of element: index * (width + gap) + width / 2
- const offset = pointer.index * (elementWidth + gap) + elementWidth / 2;
+ // Calculate center of element: index * (width + gap) + width / 2, plus any offset for overlapping
+ const offset = pointer.index * (elementWidth + gap) + elementWidth / 2 + horizontalOffset;
return (
- {/* Label - centered above the point */}
+ {/* Label - centered above the arrow */}
{pointer.name}
- {pointer.showValue && value !== undefined && (
+ {!hideValue && pointer.showValue && value !== undefined && (
= {value}
)}
diff --git a/frontend/src/content/algorithms/fast-slow-pointers.ts b/frontend/src/content/algorithms/fast-slow-pointers.ts
new file mode 100644
index 0000000..d0f5619
--- /dev/null
+++ b/frontend/src/content/algorithms/fast-slow-pointers.ts
@@ -0,0 +1,389 @@
+import type { AlgorithmDefinition } from '@/lib/visualizations/types';
+
+export const fastSlowPointersAlgorithm: AlgorithmDefinition = {
+ id: 'fast-slow-pointers',
+ title: 'Find Middle of Linked List',
+ slug: 'fast-slow-pointers',
+ pattern: {
+ name: 'Fast & Slow Pointers',
+ description: 'Use two pointers moving at different speeds to find cycle or middle',
+ },
+ problemStatement:
+ 'Given a linked list, find the middle node. If there are two middle nodes, return the second one.',
+ intuition:
+ 'If fast pointer moves twice as fast as slow, when fast reaches the end, slow will be at the middle.',
+ code: {
+ language: 'python',
+ code: `def find_middle(head):
+ slow = fast = head
+
+ while fast and fast.next:
+ slow = slow.next
+ fast = fast.next.next
+
+ return slow`,
+ },
+ steps: [
+ // Problem phase
+ {
+ id: 'step-1',
+ phase: 'problem',
+ explanation: 'We have a linked list: 1 → 2 → 3 → 4 → 5 → null. Find the middle node.',
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'normal' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [],
+ },
+ },
+ // Intuition phase
+ {
+ id: 'step-2',
+ phase: 'intuition',
+ explanation:
+ 'The key insight: if one pointer moves 2x faster than another, when the fast one finishes, the slow one is halfway.',
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'normal' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'highlighted' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [],
+ },
+ },
+ // Code phase
+ {
+ id: 'step-3',
+ phase: 'code',
+ explanation: 'Initialize both pointers at the head of the list.',
+ codeLine: 2,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'head' },
+ { id: 'fast', name: 'fast', value: 'head' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 0, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 0, color: 'fast' },
+ ],
+ },
+ },
+ // Execution phase - iteration 1
+ {
+ id: 'step-4',
+ phase: 'execution',
+ explanation: 'Check: fast (node 1) exists and fast.next (node 2) exists. Enter loop.',
+ codeLine: 4,
+ decision: {
+ question: 'Is fast and fast.next valid?',
+ answer: 'Yes, both exist',
+ action: 'Enter the while loop',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 1' },
+ { id: 'fast', name: 'fast', value: 'node 1' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 0, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 0, color: 'fast' },
+ ],
+ },
+ },
+ {
+ id: 'step-5',
+ phase: 'execution',
+ explanation: 'Move slow one step forward (1 → 2).',
+ codeLine: 5,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 2', previousValue: 'node 1' },
+ { id: 'fast', name: 'fast', value: 'node 1' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 1, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 0, color: 'fast' },
+ ],
+ },
+ },
+ {
+ id: 'step-6',
+ phase: 'execution',
+ explanation: 'Move fast two steps forward (1 → 2 → 3).',
+ codeLine: 6,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 2' },
+ { id: 'fast', name: 'fast', value: 'node 3', previousValue: 'node 1' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'comparing' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 1, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 2, color: 'fast' },
+ ],
+ },
+ },
+ // Iteration 2
+ {
+ id: 'step-7',
+ phase: 'execution',
+ explanation: 'Check: fast (node 3) exists and fast.next (node 4) exists. Continue loop.',
+ codeLine: 4,
+ decision: {
+ question: 'Is fast and fast.next valid?',
+ answer: 'Yes, both exist',
+ action: 'Continue the while loop',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 2' },
+ { id: 'fast', name: 'fast', value: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'comparing' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 1, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 2, color: 'fast' },
+ ],
+ },
+ },
+ {
+ id: 'step-8',
+ phase: 'execution',
+ explanation: 'Move slow one step forward (2 → 3).',
+ codeLine: 5,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 3', previousValue: 'node 2' },
+ { id: 'fast', name: 'fast', value: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'dimmed' },
+ { id: 'n3', value: 3, index: 2, state: 'highlighted' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'n5', value: 5, index: 4, state: 'normal' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 2, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 2, color: 'fast' },
+ ],
+ },
+ },
+ {
+ id: 'step-9',
+ phase: 'execution',
+ explanation: 'Move fast two steps forward (3 → 4 → 5).',
+ codeLine: 6,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 3' },
+ { id: 'fast', name: 'fast', value: 'node 5', previousValue: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'dimmed' },
+ { id: 'n3', value: 3, index: 2, state: 'highlighted' },
+ { id: 'n4', value: 4, index: 3, state: 'dimmed' },
+ { id: 'n5', value: 5, index: 4, state: 'comparing' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 2, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 4, color: 'fast' },
+ ],
+ },
+ },
+ // Check and exit
+ {
+ id: 'step-10',
+ phase: 'execution',
+ explanation: 'Check: fast (node 5) exists but fast.next is null. Exit loop.',
+ codeLine: 4,
+ decision: {
+ question: 'Is fast and fast.next valid?',
+ answer: 'No, fast.next is null',
+ action: 'Exit the while loop',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 3' },
+ { id: 'fast', name: 'fast', value: 'node 5' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'dimmed' },
+ { id: 'n3', value: 3, index: 2, state: 'highlighted' },
+ { id: 'n4', value: 4, index: 3, state: 'dimmed' },
+ { id: 'n5', value: 5, index: 4, state: 'comparing' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 2, color: 'slow' },
+ { id: 'fast', name: 'fast', nodeIndex: 4, color: 'fast' },
+ ],
+ },
+ },
+ // Return result
+ {
+ id: 'step-11',
+ phase: 'execution',
+ explanation: 'Return slow, which points to node 3 — the middle of the linked list!',
+ codeLine: 8,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'slow', name: 'slow', value: 'node 3' },
+ { id: 'fast', name: 'fast', value: 'node 5' },
+ { id: 'result', name: 'result', value: 3 },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'dimmed' },
+ { id: 'n2', value: 2, index: 1, state: 'dimmed' },
+ { id: 'n3', value: 3, index: 2, state: 'success' },
+ { id: 'n4', value: 4, index: 3, state: 'dimmed' },
+ { id: 'n5', value: 5, index: 4, state: 'dimmed' },
+ { id: 'null', value: 0, index: 5, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'slow', name: 'slow', nodeIndex: 2, color: 'slow' },
+ ],
+ },
+ },
+ ],
+};
diff --git a/frontend/src/content/algorithms/linkedlist-reversal.ts b/frontend/src/content/algorithms/linkedlist-reversal.ts
new file mode 100644
index 0000000..abfb253
--- /dev/null
+++ b/frontend/src/content/algorithms/linkedlist-reversal.ts
@@ -0,0 +1,515 @@
+import type { AlgorithmDefinition } from '@/lib/visualizations/types';
+
+export const linkedListReversalAlgorithm: AlgorithmDefinition = {
+ id: 'linkedlist-reversal',
+ title: 'Reverse a Linked List',
+ slug: 'linkedlist-reversal',
+ pattern: {
+ name: 'LinkedList Reversal',
+ description: 'Reverse links between nodes using three pointers',
+ },
+ problemStatement:
+ 'Given a singly linked list, reverse it in-place and return the new head.',
+ intuition:
+ 'Use three pointers (prev, curr, next) to reverse each link. Save next before breaking the link, then move forward.',
+ code: {
+ language: 'python',
+ code: `def reverse_list(head):
+ prev = None
+ curr = head
+
+ while curr:
+ next_node = curr.next
+ curr.next = prev
+ prev = curr
+ curr = next_node
+
+ return prev`,
+ },
+ steps: [
+ // Problem phase
+ {
+ id: 'step-1',
+ phase: 'problem',
+ explanation: 'We have a linked list: 1 → 2 → 3 → 4 → null. Reverse it to get 4 → 3 → 2 → 1 → null.',
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'normal' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [],
+ },
+ },
+ // Intuition phase
+ {
+ id: 'step-2',
+ phase: 'intuition',
+ explanation:
+ 'Key insight: We need to reverse each arrow. To do this safely, save the next node before changing the link.',
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'comparing' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [],
+ },
+ },
+ // Code phase - initialization
+ {
+ id: 'step-3',
+ phase: 'code',
+ explanation: 'Initialize prev = None (nothing before head yet) and curr = head.',
+ codeLine: 2,
+ codeHighlightLines: [2, 3],
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'None' },
+ { id: 'curr', name: 'curr', value: 'node 1' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'curr', name: 'curr', nodeIndex: 0, color: 'curr' },
+ ],
+ },
+ },
+ // Execution - iteration 1
+ {
+ id: 'step-4',
+ phase: 'execution',
+ explanation: 'Check: curr (node 1) exists. Enter the while loop.',
+ codeLine: 5,
+ decision: {
+ question: 'Is curr not None?',
+ answer: 'Yes, curr is node 1',
+ action: 'Enter the while loop',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'None' },
+ { id: 'curr', name: 'curr', value: 'node 1' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'normal' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'curr', name: 'curr', nodeIndex: 0, color: 'curr' },
+ ],
+ },
+ },
+ {
+ id: 'step-5',
+ phase: 'execution',
+ explanation: 'Save next_node = curr.next (node 2). This preserves our forward reference.',
+ codeLine: 6,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'None' },
+ { id: 'curr', name: 'curr', value: 'node 1' },
+ { id: 'next', name: 'next', value: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'highlighted' },
+ { id: 'n2', value: 2, index: 1, state: 'comparing' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'curr', name: 'curr', nodeIndex: 0, color: 'curr' },
+ { id: 'next', name: 'next', nodeIndex: 1, color: 'next' },
+ ],
+ },
+ },
+ {
+ id: 'step-6',
+ phase: 'execution',
+ explanation: 'Reverse the link: curr.next = prev. Node 1 now points to None (backwards!).',
+ codeLine: 7,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'None' },
+ { id: 'curr', name: 'curr', value: 'node 1' },
+ { id: 'next', name: 'next', value: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'comparing' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'curr', name: 'curr', nodeIndex: 0, color: 'curr' },
+ { id: 'next', name: 'next', nodeIndex: 1, color: 'next' },
+ ],
+ },
+ },
+ {
+ id: 'step-7',
+ phase: 'execution',
+ explanation: 'Move prev forward: prev = curr. Now prev points to node 1.',
+ codeLine: 8,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 1', previousValue: 'None' },
+ { id: 'curr', name: 'curr', value: 'node 1' },
+ { id: 'next', name: 'next', value: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'comparing' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 0, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 0, color: 'curr' },
+ { id: 'next', name: 'next', nodeIndex: 1, color: 'next' },
+ ],
+ },
+ },
+ {
+ id: 'step-8',
+ phase: 'execution',
+ explanation: 'Move curr forward: curr = next_node. Now curr points to node 2.',
+ codeLine: 9,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 1' },
+ { id: 'curr', name: 'curr', value: 'node 2', previousValue: 'node 1' },
+ { id: 'next', name: 'next', value: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 0, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 1, color: 'curr' },
+ ],
+ },
+ },
+ // Iteration 2
+ {
+ id: 'step-9',
+ phase: 'execution',
+ explanation: 'Check: curr (node 2) exists. Continue loop.',
+ codeLine: 5,
+ decision: {
+ question: 'Is curr not None?',
+ answer: 'Yes, curr is node 2',
+ action: 'Continue the while loop',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 1' },
+ { id: 'curr', name: 'curr', value: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'normal' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 0, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 1, color: 'curr' },
+ ],
+ },
+ },
+ {
+ id: 'step-10',
+ phase: 'execution',
+ explanation: 'Save next_node = curr.next (node 3).',
+ codeLine: 6,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 1' },
+ { id: 'curr', name: 'curr', value: 'node 2' },
+ { id: 'next', name: 'next', value: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'highlighted' },
+ { id: 'n3', value: 3, index: 2, state: 'comparing' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 0, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 1, color: 'curr' },
+ { id: 'next', name: 'next', nodeIndex: 2, color: 'next' },
+ ],
+ },
+ },
+ {
+ id: 'step-11',
+ phase: 'execution',
+ explanation: 'Reverse the link: curr.next = prev. Node 2 now points to node 1.',
+ codeLine: 7,
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 1' },
+ { id: 'curr', name: 'curr', value: 'node 2' },
+ { id: 'next', name: 'next', value: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'success' },
+ { id: 'n3', value: 3, index: 2, state: 'comparing' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 0, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 1, color: 'curr' },
+ { id: 'next', name: 'next', nodeIndex: 2, color: 'next' },
+ ],
+ },
+ },
+ {
+ id: 'step-12',
+ phase: 'execution',
+ explanation: 'Move prev = curr, curr = next_node. Advance to process node 3.',
+ codeLine: 8,
+ codeHighlightLines: [8, 9],
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 2', previousValue: 'node 1' },
+ { id: 'curr', name: 'curr', value: 'node 3', previousValue: 'node 2' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'success' },
+ { id: 'n3', value: 3, index: 2, state: 'highlighted' },
+ { id: 'n4', value: 4, index: 3, state: 'normal' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 1, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 2, color: 'curr' },
+ ],
+ },
+ },
+ // Iteration 3 (condensed)
+ {
+ id: 'step-13',
+ phase: 'execution',
+ explanation: 'Process node 3: save next (node 4), reverse link (3→2), advance pointers.',
+ codeLine: 6,
+ codeHighlightLines: [6, 7, 8, 9],
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 3', previousValue: 'node 2' },
+ { id: 'curr', name: 'curr', value: 'node 4', previousValue: 'node 3' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'success' },
+ { id: 'n3', value: 3, index: 2, state: 'success' },
+ { id: 'n4', value: 4, index: 3, state: 'highlighted' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 2, color: 'prev' },
+ { id: 'curr', name: 'curr', nodeIndex: 3, color: 'curr' },
+ ],
+ },
+ },
+ // Iteration 4 (last node)
+ {
+ id: 'step-14',
+ phase: 'execution',
+ explanation: 'Process node 4: save next (null), reverse link (4→3), advance pointers.',
+ codeLine: 6,
+ codeHighlightLines: [6, 7, 8, 9],
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 4', previousValue: 'node 3' },
+ { id: 'curr', name: 'curr', value: 'None', previousValue: 'node 4' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ nodes: [
+ { id: 'n1', value: 1, index: 0, state: 'success' },
+ { id: 'n2', value: 2, index: 1, state: 'success' },
+ { id: 'n3', value: 3, index: 2, state: 'success' },
+ { id: 'n4', value: 4, index: 3, state: 'success' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [
+ { id: 'prev', name: 'prev', nodeIndex: 3, color: 'prev' },
+ ],
+ },
+ },
+ // Exit and return
+ {
+ id: 'step-15',
+ phase: 'execution',
+ explanation: 'Check: curr is None. Exit loop. Return prev (node 4), the new head!',
+ codeLine: 11,
+ decision: {
+ question: 'Is curr not None?',
+ answer: 'No, curr is None',
+ action: 'Exit loop, return prev',
+ },
+ dataState: {
+ arrays: [],
+ pointers: [],
+ variables: [
+ { id: 'prev', name: 'prev', value: 'node 4' },
+ { id: 'curr', name: 'curr', value: 'None' },
+ { id: 'result', name: 'result', value: 'node 4 (new head)' },
+ ],
+ calculations: [],
+ linkedLists: [
+ {
+ id: 'list',
+ label: 'Reversed: 4 → 3 → 2 → 1 → null',
+ nodes: [
+ { id: 'n4', value: 4, index: 0, state: 'success' },
+ { id: 'n3', value: 3, index: 1, state: 'success' },
+ { id: 'n2', value: 2, index: 2, state: 'success' },
+ { id: 'n1', value: 1, index: 3, state: 'success' },
+ { id: 'null', value: 0, index: 4, state: 'normal', isNull: true },
+ ],
+ },
+ ],
+ linkedListPointers: [],
+ },
+ },
+ ],
+};
diff --git a/frontend/src/lib/visualizations/types.ts b/frontend/src/lib/visualizations/types.ts
index a8eafec..5639aae 100644
--- a/frontend/src/lib/visualizations/types.ts
+++ b/frontend/src/lib/visualizations/types.ts
@@ -77,6 +77,11 @@ export interface DataState {
pointers: PointerState[];
variables: VariableState[];
calculations: CalculationState[];
+ // LinkedList support
+ linkedLists?: LinkedListState[];
+ linkedListPointers?: LinkedListPointerState[];
+ // Stack support
+ stacks?: StackState[];
}
/** Single step in the visualization */
@@ -152,3 +157,49 @@ export interface UseVisualizationReturn {
currentPhase: VisualizationPhase;
progress: number;
}
+
+// ============================================
+// LinkedList Types
+// ============================================
+
+/** State of a linked list node */
+export interface LinkedListNodeState {
+ id: string;
+ value: number;
+ index: number;
+ state: 'normal' | 'highlighted' | 'dimmed' | 'success' | 'comparing';
+ isNull?: boolean;
+}
+
+/** Complete linked list state */
+export interface LinkedListState {
+ id: string;
+ nodes: LinkedListNodeState[];
+ label?: string;
+}
+
+/** Pointer for linked list visualization */
+export interface LinkedListPointerState {
+ id: string;
+ name: string;
+ nodeIndex: number;
+ color: 'slow' | 'fast' | 'prev' | 'curr' | 'next' | 'default';
+}
+
+// ============================================
+// Stack Types
+// ============================================
+
+/** State of a stack element */
+export interface StackElementState {
+ id: string;
+ value: number | string;
+ state: 'normal' | 'highlighted' | 'popped' | 'pushing';
+}
+
+/** Complete stack state */
+export interface StackState {
+ id: string;
+ elements: StackElementState[];
+ label?: string;
+}