linked list visualisations
This commit is contained in:
@@ -14,11 +14,13 @@ import {
|
|||||||
RelatedPatterns,
|
RelatedPatterns,
|
||||||
} from "@/components/patterns";
|
} from "@/components/patterns";
|
||||||
import { PatternVisualization } from "@/components/visualization";
|
import { PatternVisualization } from "@/components/visualization";
|
||||||
import { TwoPointersVisualization, PrefixSumVisualization } from "@/components/visualizations-new";
|
import { TwoPointersVisualization, PrefixSumVisualization, LinkedListVisualization } from "@/components/visualizations-new";
|
||||||
import { twoSumAlgorithm } from "@/content/algorithms/two-sum";
|
import { twoSumAlgorithm } from "@/content/algorithms/two-sum";
|
||||||
import { slidingWindowAlgorithm } from "@/content/algorithms/sliding-window";
|
import { slidingWindowAlgorithm } from "@/content/algorithms/sliding-window";
|
||||||
import { binarySearchAlgorithm } from "@/content/algorithms/binary-search";
|
import { binarySearchAlgorithm } from "@/content/algorithms/binary-search";
|
||||||
import { prefixSumAlgorithm } from "@/content/algorithms/prefix-sum";
|
import { prefixSumAlgorithm } from "@/content/algorithms/prefix-sum";
|
||||||
|
import { fastSlowPointersAlgorithm } from "@/content/algorithms/fast-slow-pointers";
|
||||||
|
import { linkedListReversalAlgorithm } from "@/content/algorithms/linkedlist-reversal";
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
@@ -119,6 +121,10 @@ export default async function PatternDetailPage({ params }: PageProps) {
|
|||||||
<TwoPointersVisualization algorithm={binarySearchAlgorithm} />
|
<TwoPointersVisualization algorithm={binarySearchAlgorithm} />
|
||||||
) : slug === "prefix-sum" ? (
|
) : slug === "prefix-sum" ? (
|
||||||
<PrefixSumVisualization algorithm={prefixSumAlgorithm} />
|
<PrefixSumVisualization algorithm={prefixSumAlgorithm} />
|
||||||
|
) : slug === "fast-slow-pointers" ? (
|
||||||
|
<LinkedListVisualization algorithm={fastSlowPointersAlgorithm} />
|
||||||
|
) : slug === "linkedlist-reversal" ? (
|
||||||
|
<LinkedListVisualization algorithm={linkedListReversalAlgorithm} />
|
||||||
) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? (
|
) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useVisualization } from '@/lib/visualizations/use-visualization';
|
||||||
|
import type { AlgorithmDefinition, LinkedListState, LinkedListPointerState } from '@/lib/visualizations/types';
|
||||||
|
import { VisualizationContainer } from '../core/visualization-container';
|
||||||
|
import { LinkedListView } from '../data-structures/linked-list-view';
|
||||||
|
|
||||||
|
interface LinkedListVisualizationProps {
|
||||||
|
algorithm: AlgorithmDefinition;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LinkedListVisualization({
|
||||||
|
algorithm,
|
||||||
|
className,
|
||||||
|
}: LinkedListVisualizationProps) {
|
||||||
|
const {
|
||||||
|
currentStep,
|
||||||
|
currentStepIndex,
|
||||||
|
totalSteps,
|
||||||
|
playback,
|
||||||
|
controls,
|
||||||
|
currentPhase,
|
||||||
|
progress,
|
||||||
|
} = useVisualization(algorithm);
|
||||||
|
|
||||||
|
const { dataState } = currentStep;
|
||||||
|
|
||||||
|
// Extract linked list and pointers from dataState
|
||||||
|
// We store linkedLists in a custom field, falling back to empty
|
||||||
|
const linkedList: LinkedListState = (dataState as { linkedLists?: LinkedListState[] }).linkedLists?.[0] ?? {
|
||||||
|
id: 'empty',
|
||||||
|
nodes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
const linkedListPointers: LinkedListPointerState[] =
|
||||||
|
(dataState as { linkedListPointers?: LinkedListPointerState[] }).linkedListPointers ?? [];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<VisualizationContainer
|
||||||
|
title={algorithm.title}
|
||||||
|
pattern={algorithm.pattern}
|
||||||
|
code={algorithm.code}
|
||||||
|
currentLine={currentStep.codeLine}
|
||||||
|
highlightLines={currentStep.codeHighlightLines}
|
||||||
|
explanation={currentStep.explanation}
|
||||||
|
decision={currentStep.decision}
|
||||||
|
phase={currentPhase}
|
||||||
|
variables={dataState.variables}
|
||||||
|
currentStepIndex={currentStepIndex}
|
||||||
|
totalSteps={totalSteps}
|
||||||
|
isPlaying={playback.isPlaying}
|
||||||
|
speed={playback.speed}
|
||||||
|
controls={controls}
|
||||||
|
progress={progress}
|
||||||
|
className={className}
|
||||||
|
>
|
||||||
|
<div className="flex flex-col items-center justify-center">
|
||||||
|
<LinkedListView
|
||||||
|
list={linkedList}
|
||||||
|
pointers={linkedListPointers}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VisualizationContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -81,7 +81,7 @@ export function VisualizationContainer({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className="flex min-h-80 items-center justify-center rounded-lg border border-border bg-surface px-8 py-4 lg:h-auto"
|
className="flex min-h-80 min-w-0 items-center justify-center overflow-x-auto rounded-lg border border-border bg-surface px-8 py-4 lg:h-auto"
|
||||||
role="img"
|
role="img"
|
||||||
aria-label="Algorithm visualization"
|
aria-label="Algorithm visualization"
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -39,6 +39,30 @@ export function ArrayView({
|
|||||||
// Calculate total array width for proper pointer alignment
|
// Calculate total array width for proper pointer alignment
|
||||||
const totalWidth = array.elements.length * elementWidth + (array.elements.length - 1) * gap;
|
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<number, PointerState[]>);
|
||||||
|
|
||||||
|
// 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 (
|
return (
|
||||||
<div className={cn("flex flex-col items-center", className)}>
|
<div className={cn("flex flex-col items-center", className)}>
|
||||||
{array.label && (
|
{array.label && (
|
||||||
@@ -58,6 +82,8 @@ export function ArrayView({
|
|||||||
elementWidth={elementWidth}
|
elementWidth={elementWidth}
|
||||||
gap={gap}
|
gap={gap}
|
||||||
value={array.elements[pointer.index]?.value}
|
value={array.elements[pointer.index]?.value}
|
||||||
|
hideValue={hasClosePointers}
|
||||||
|
horizontalOffset={getPointerOffset(pointer)}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -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<number, LinkedListPointerState[]>);
|
||||||
|
|
||||||
|
// 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 (
|
||||||
|
<div className={cn('flex flex-col items-center', className)}>
|
||||||
|
{list.label && (
|
||||||
|
<span className="mb-2 text-sm font-medium text-foreground-muted">
|
||||||
|
{list.label}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div className="relative" style={{ width: totalWidth }}>
|
||||||
|
{/* Pointers row */}
|
||||||
|
<div className="relative mb-1 h-8">
|
||||||
|
{pointers.map((pointer) => (
|
||||||
|
<LinkedListPointer
|
||||||
|
key={pointer.id}
|
||||||
|
pointer={pointer}
|
||||||
|
nodeWidth={NODE_WIDTH}
|
||||||
|
arrowWidth={ARROW_WIDTH}
|
||||||
|
horizontalOffset={getPointerOffset(pointer)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Linked list nodes */}
|
||||||
|
<div className="flex items-center">
|
||||||
|
{list.nodes.map((node, index) => (
|
||||||
|
<LinkedListNode
|
||||||
|
key={node.id}
|
||||||
|
node={node}
|
||||||
|
isLast={index === list.nodes.length - 1}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,10 +9,14 @@ export { VariableInspector } from "./core/variable-inspector";
|
|||||||
export { ArrayElement } from "./primitives/array-element";
|
export { ArrayElement } from "./primitives/array-element";
|
||||||
export { Pointer } from "./primitives/pointer";
|
export { Pointer } from "./primitives/pointer";
|
||||||
export { CalculationBubble } from "./primitives/calculation-bubble";
|
export { CalculationBubble } from "./primitives/calculation-bubble";
|
||||||
|
export { LinkedListNode } from "./primitives/linked-list-node";
|
||||||
|
export { LinkedListPointer } from "./primitives/linked-list-pointer";
|
||||||
|
|
||||||
// Data structures
|
// Data structures
|
||||||
export { ArrayView } from "./data-structures/array-view";
|
export { ArrayView } from "./data-structures/array-view";
|
||||||
|
export { LinkedListView } from "./data-structures/linked-list-view";
|
||||||
|
|
||||||
// Algorithm visualizations
|
// Algorithm visualizations
|
||||||
export { PrefixSumVisualization } from "./algorithms/prefix-sum";
|
export { PrefixSumVisualization } from "./algorithms/prefix-sum";
|
||||||
export { TwoPointersVisualization } from "./algorithms/two-pointers";
|
export { TwoPointersVisualization } from "./algorithms/two-pointers";
|
||||||
|
export { LinkedListVisualization } from "./algorithms/linked-list";
|
||||||
|
|||||||
@@ -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 (
|
||||||
|
<div className={cn('flex items-center', className)}>
|
||||||
|
<div className="flex h-10 items-center justify-center px-3 font-mono text-sm text-foreground-muted">
|
||||||
|
null
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={cn('flex items-center', className)}>
|
||||||
|
<motion.div
|
||||||
|
layout
|
||||||
|
initial={false}
|
||||||
|
animate={{
|
||||||
|
scale: node.state === 'highlighted' ? 1.05 : 1,
|
||||||
|
}}
|
||||||
|
transition={{ duration: 0.2 }}
|
||||||
|
className={cn(
|
||||||
|
'flex h-10 w-10 items-center justify-center rounded-lg border-2 font-mono font-medium transition-colors duration-200',
|
||||||
|
STATE_CLASSES[node.state]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{node.value}
|
||||||
|
</motion.div>
|
||||||
|
{!isLast && (
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{ opacity: 1 }}
|
||||||
|
className="flex items-center"
|
||||||
|
>
|
||||||
|
<div className="h-0.5 w-4 bg-border" />
|
||||||
|
<div className="h-0 w-0 border-y-4 border-l-6 border-y-transparent border-l-border" />
|
||||||
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -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 (
|
||||||
|
<motion.div
|
||||||
|
initial={false}
|
||||||
|
animate={{ x: x - 12 }}
|
||||||
|
transition={{
|
||||||
|
type: 'spring',
|
||||||
|
stiffness: 300,
|
||||||
|
damping: 30,
|
||||||
|
}}
|
||||||
|
className={cn(
|
||||||
|
'absolute top-0 flex flex-col items-center',
|
||||||
|
COLOR_CLASSES[pointer.color]
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<span className="text-xs font-medium">{pointer.name}</span>
|
||||||
|
<svg
|
||||||
|
width="16"
|
||||||
|
height="12"
|
||||||
|
viewBox="0 0 16 12"
|
||||||
|
fill="currentColor"
|
||||||
|
className="mt-0.5"
|
||||||
|
>
|
||||||
|
<path d="M8 12L0 0h16L8 12z" />
|
||||||
|
</svg>
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -9,6 +9,10 @@ interface PointerProps {
|
|||||||
elementWidth: number;
|
elementWidth: number;
|
||||||
gap: number;
|
gap: number;
|
||||||
value?: 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;
|
className?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -24,10 +28,12 @@ export function Pointer({
|
|||||||
elementWidth,
|
elementWidth,
|
||||||
gap,
|
gap,
|
||||||
value,
|
value,
|
||||||
|
hideValue = false,
|
||||||
|
horizontalOffset = 0,
|
||||||
className,
|
className,
|
||||||
}: PointerProps) {
|
}: PointerProps) {
|
||||||
// Calculate center of element: index * (width + gap) + width / 2
|
// Calculate center of element: index * (width + gap) + width / 2, plus any offset for overlapping
|
||||||
const offset = pointer.index * (elementWidth + gap) + elementWidth / 2;
|
const offset = pointer.index * (elementWidth + gap) + elementWidth / 2 + horizontalOffset;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
@@ -40,7 +46,7 @@ export function Pointer({
|
|||||||
}}
|
}}
|
||||||
className={cn("absolute top-0 h-full", className)}
|
className={cn("absolute top-0 h-full", className)}
|
||||||
>
|
>
|
||||||
{/* Label - centered above the point */}
|
{/* Label - centered above the arrow */}
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"absolute left-0 top-0 -translate-x-1/2 whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-medium",
|
"absolute left-0 top-0 -translate-x-1/2 whitespace-nowrap rounded-full px-2 py-0.5 text-xs font-medium",
|
||||||
@@ -48,7 +54,7 @@ export function Pointer({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{pointer.name}
|
{pointer.name}
|
||||||
{pointer.showValue && value !== undefined && (
|
{!hideValue && pointer.showValue && value !== undefined && (
|
||||||
<span className="ml-1 font-mono">= {value}</span>
|
<span className="ml-1 font-mono">= {value}</span>
|
||||||
)}
|
)}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
389
frontend/src/content/algorithms/fast-slow-pointers.ts
Normal file
389
frontend/src/content/algorithms/fast-slow-pointers.ts
Normal file
@@ -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' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
515
frontend/src/content/algorithms/linkedlist-reversal.ts
Normal file
515
frontend/src/content/algorithms/linkedlist-reversal.ts
Normal file
@@ -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: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -77,6 +77,11 @@ export interface DataState {
|
|||||||
pointers: PointerState[];
|
pointers: PointerState[];
|
||||||
variables: VariableState[];
|
variables: VariableState[];
|
||||||
calculations: CalculationState[];
|
calculations: CalculationState[];
|
||||||
|
// LinkedList support
|
||||||
|
linkedLists?: LinkedListState[];
|
||||||
|
linkedListPointers?: LinkedListPointerState[];
|
||||||
|
// Stack support
|
||||||
|
stacks?: StackState[];
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Single step in the visualization */
|
/** Single step in the visualization */
|
||||||
@@ -152,3 +157,49 @@ export interface UseVisualizationReturn {
|
|||||||
currentPhase: VisualizationPhase;
|
currentPhase: VisualizationPhase;
|
||||||
progress: number;
|
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;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user