feat(viz): sprint 1 - array visualisations
This commit is contained in:
@@ -1,19 +1,71 @@
|
|||||||
@import "tailwindcss";
|
@import "tailwindcss";
|
||||||
|
|
||||||
|
/* Register theme tokens with Tailwind so utility classes work */
|
||||||
|
@theme {
|
||||||
|
--color-background: var(--background);
|
||||||
|
--color-background-subtle: var(--background-subtle);
|
||||||
|
--color-surface: var(--surface);
|
||||||
|
--color-surface-variant: var(--surface-variant);
|
||||||
|
--color-surface-subtle: var(--surface-subtle);
|
||||||
|
--color-border: var(--border);
|
||||||
|
--color-border-focus: var(--border-focus);
|
||||||
|
--color-border-disabled: var(--border-disabled);
|
||||||
|
--color-foreground: var(--foreground);
|
||||||
|
--color-foreground-muted: var(--foreground-muted);
|
||||||
|
--color-primary: var(--primary);
|
||||||
|
--color-primary-hover: var(--primary-hover);
|
||||||
|
--color-primary-muted: var(--primary-muted);
|
||||||
|
--color-accent: var(--accent);
|
||||||
|
|
||||||
|
/* Semantic status colors */
|
||||||
|
--color-success: var(--success);
|
||||||
|
--color-warning: var(--warning);
|
||||||
|
--color-error: var(--error);
|
||||||
|
--color-info: var(--info);
|
||||||
|
|
||||||
|
/* Visualization pointer colors */
|
||||||
|
--color-viz-pointer-left: var(--viz-pointer-left);
|
||||||
|
--color-viz-pointer-right: var(--viz-pointer-right);
|
||||||
|
--color-viz-pointer-mid: var(--viz-pointer-mid);
|
||||||
|
--color-viz-pointer-default: var(--viz-pointer-default);
|
||||||
|
--color-viz-success: var(--viz-success);
|
||||||
|
--color-viz-compare: var(--viz-compare);
|
||||||
|
--color-viz-highlight: var(--viz-highlight);
|
||||||
|
|
||||||
|
--font-sans: var(--font-inter);
|
||||||
|
--font-mono: var(--font-jetbrains-mono);
|
||||||
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--background: #ffffff;
|
--background: #ffffff;
|
||||||
|
--background-subtle: #f5f5f5;
|
||||||
--foreground: #171717;
|
--foreground: #171717;
|
||||||
|
--foreground-muted: #5a5a5a;
|
||||||
|
--surface: #ffffff;
|
||||||
|
--surface-variant: #f5f5f5;
|
||||||
|
--surface-subtle: #fafafa;
|
||||||
--card: #ffffff;
|
--card: #ffffff;
|
||||||
--card-foreground: #171717;
|
--card-foreground: #171717;
|
||||||
--primary: #3b82f6;
|
--primary: #3b82f6;
|
||||||
--primary-foreground: #ffffff;
|
--primary-foreground: #ffffff;
|
||||||
|
--primary-hover: #2563eb;
|
||||||
|
--primary-muted: #dbeafe;
|
||||||
--secondary: #f3f4f6;
|
--secondary: #f3f4f6;
|
||||||
--secondary-foreground: #171717;
|
--secondary-foreground: #171717;
|
||||||
--muted: #f3f4f6;
|
--muted: #f3f4f6;
|
||||||
--muted-foreground: #5f6368;
|
--muted-foreground: #5f6368;
|
||||||
|
--accent: #0891b2;
|
||||||
--border: #e5e7eb;
|
--border: #e5e7eb;
|
||||||
|
--border-focus: var(--primary);
|
||||||
|
--border-disabled: #d4d4d4;
|
||||||
--ring: #3b82f6;
|
--ring: #3b82f6;
|
||||||
|
|
||||||
|
/* Semantic status colors */
|
||||||
|
--success: #059669;
|
||||||
|
--warning: #d97706;
|
||||||
|
--error: #dc2626;
|
||||||
|
--info: #2563eb;
|
||||||
|
|
||||||
/* Difficulty colors */
|
/* Difficulty colors */
|
||||||
--difficulty-easy: #16a34a;
|
--difficulty-easy: #16a34a;
|
||||||
--difficulty-easy-bg: #dcfce7;
|
--difficulty-easy-bg: #dcfce7;
|
||||||
@@ -70,23 +122,54 @@
|
|||||||
--viz-visited: #9ca3af;
|
--viz-visited: #9ca3af;
|
||||||
--viz-swapping: #8b5cf6;
|
--viz-swapping: #8b5cf6;
|
||||||
--viz-transition: 300ms;
|
--viz-transition: 300ms;
|
||||||
|
|
||||||
|
/* Visualization pointer colors */
|
||||||
|
--viz-pointer-left: #2563eb;
|
||||||
|
--viz-pointer-right: #ea580c;
|
||||||
|
--viz-pointer-mid: #9333ea;
|
||||||
|
--viz-pointer-default: #4b5563;
|
||||||
|
--viz-success: #16a34a;
|
||||||
|
--viz-compare: #eab308;
|
||||||
|
--viz-highlight: #4f46e5;
|
||||||
|
--viz-dimmed-opacity: 0.3;
|
||||||
|
|
||||||
|
/* Visualization timing */
|
||||||
|
--viz-duration-fast: 200ms;
|
||||||
|
--viz-duration-normal: 400ms;
|
||||||
|
--viz-duration-slow: 600ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
:root {
|
:root {
|
||||||
--background: #0a0a0a;
|
--background: #0a0a0a;
|
||||||
--foreground: #ededed;
|
--background-subtle: #121212;
|
||||||
|
--foreground: #e5e5e5;
|
||||||
|
--foreground-muted: #b0b0b0;
|
||||||
|
--surface: #141414;
|
||||||
|
--surface-variant: #1e1e1e;
|
||||||
|
--surface-subtle: #0f0f0f;
|
||||||
--card: #171717;
|
--card: #171717;
|
||||||
--card-foreground: #ededed;
|
--card-foreground: #ededed;
|
||||||
--primary: #3b82f6;
|
--primary: #6366f1;
|
||||||
--primary-foreground: #ffffff;
|
--primary-foreground: #ffffff;
|
||||||
|
--primary-hover: #818cf8;
|
||||||
|
--primary-muted: #1e1b4b;
|
||||||
--secondary: #262626;
|
--secondary: #262626;
|
||||||
--secondary-foreground: #ededed;
|
--secondary-foreground: #ededed;
|
||||||
--muted: #262626;
|
--muted: #262626;
|
||||||
--muted-foreground: #a3a3a3;
|
--muted-foreground: #a3a3a3;
|
||||||
|
--accent: #22d3ee;
|
||||||
--border: #262626;
|
--border: #262626;
|
||||||
|
--border-focus: var(--primary);
|
||||||
|
--border-disabled: #404040;
|
||||||
--ring: #3b82f6;
|
--ring: #3b82f6;
|
||||||
|
|
||||||
|
/* Semantic status colors */
|
||||||
|
--success: #10b981;
|
||||||
|
--warning: #f59e0b;
|
||||||
|
--error: #ef4444;
|
||||||
|
--info: #3b82f6;
|
||||||
|
|
||||||
/* Difficulty colors (dark mode) */
|
/* Difficulty colors (dark mode) */
|
||||||
--difficulty-easy: #4ade80;
|
--difficulty-easy: #4ade80;
|
||||||
--difficulty-easy-bg: rgba(34, 197, 94, 0.2);
|
--difficulty-easy-bg: rgba(34, 197, 94, 0.2);
|
||||||
@@ -142,6 +225,21 @@
|
|||||||
--viz-found: #22c55e;
|
--viz-found: #22c55e;
|
||||||
--viz-visited: #6b7280;
|
--viz-visited: #6b7280;
|
||||||
--viz-swapping: #8b5cf6;
|
--viz-swapping: #8b5cf6;
|
||||||
|
|
||||||
|
/* Visualization pointer colors (dark mode) */
|
||||||
|
--viz-pointer-left: #3b82f6;
|
||||||
|
--viz-pointer-right: #f97316;
|
||||||
|
--viz-pointer-mid: #a855f7;
|
||||||
|
--viz-pointer-default: #6b7280;
|
||||||
|
--viz-success: #22c55e;
|
||||||
|
--viz-compare: #fbbf24;
|
||||||
|
--viz-highlight: #6366f1;
|
||||||
|
--viz-dimmed-opacity: 0.3;
|
||||||
|
|
||||||
|
/* Visualization timing */
|
||||||
|
--viz-duration-fast: 200ms;
|
||||||
|
--viz-duration-normal: 400ms;
|
||||||
|
--viz-duration-slow: 600ms;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ import {
|
|||||||
RelatedPatterns,
|
RelatedPatterns,
|
||||||
} from "@/components/patterns";
|
} from "@/components/patterns";
|
||||||
import { PatternVisualization } from "@/components/visualization";
|
import { PatternVisualization } from "@/components/visualization";
|
||||||
import { TwoPointersVisualization } from "@/components/visualizations-new";
|
import { TwoPointersVisualization, PrefixSumVisualization } 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 { binarySearchAlgorithm } from "@/content/algorithms/binary-search";
|
||||||
|
import { prefixSumAlgorithm } from "@/content/algorithms/prefix-sum";
|
||||||
|
|
||||||
interface PageProps {
|
interface PageProps {
|
||||||
params: Promise<{ slug: string }>;
|
params: Promise<{ slug: string }>;
|
||||||
@@ -109,14 +112,13 @@ export default async function PatternDetailPage({ params }: PageProps) {
|
|||||||
|
|
||||||
{/* Interactive Visualization */}
|
{/* Interactive Visualization */}
|
||||||
{slug === "two-pointers" ? (
|
{slug === "two-pointers" ? (
|
||||||
<Card>
|
|
||||||
<CardHeader>
|
|
||||||
<CardTitle>Interactive Visualization</CardTitle>
|
|
||||||
</CardHeader>
|
|
||||||
<CardContent>
|
|
||||||
<TwoPointersVisualization algorithm={twoSumAlgorithm} />
|
<TwoPointersVisualization algorithm={twoSumAlgorithm} />
|
||||||
</CardContent>
|
) : slug === "sliding-window" ? (
|
||||||
</Card>
|
<TwoPointersVisualization algorithm={slidingWindowAlgorithm} />
|
||||||
|
) : slug === "binary-search" ? (
|
||||||
|
<TwoPointersVisualization algorithm={binarySearchAlgorithm} />
|
||||||
|
) : slug === "prefix-sum" ? (
|
||||||
|
<PrefixSumVisualization algorithm={prefixSumAlgorithm} />
|
||||||
) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? (
|
) : pattern.visualization_examples && pattern.visualization_examples.length > 0 ? (
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
|
|||||||
@@ -0,0 +1,103 @@
|
|||||||
|
'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 { CalculationBubble } from '../primitives/calculation-bubble';
|
||||||
|
|
||||||
|
interface PrefixSumVisualizationProps {
|
||||||
|
algorithm: AlgorithmDefinition;
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function PrefixSumVisualization({
|
||||||
|
algorithm,
|
||||||
|
className,
|
||||||
|
}: PrefixSumVisualizationProps) {
|
||||||
|
const {
|
||||||
|
currentStep,
|
||||||
|
currentStepIndex,
|
||||||
|
totalSteps,
|
||||||
|
playback,
|
||||||
|
controls,
|
||||||
|
currentPhase,
|
||||||
|
progress,
|
||||||
|
} = useVisualization(algorithm);
|
||||||
|
|
||||||
|
const { dataState } = currentStep;
|
||||||
|
const originalArray = dataState.arrays[0];
|
||||||
|
const prefixArray = dataState.arrays[1] ?? null;
|
||||||
|
const calculation = dataState.calculations[0] ?? null;
|
||||||
|
|
||||||
|
// Filter pointers for each array
|
||||||
|
// Pointers for original array: those with index < original array length
|
||||||
|
// Pointers for prefix array: shown on prefix array if it exists
|
||||||
|
const originalPointers = prefixArray
|
||||||
|
? dataState.pointers.filter(
|
||||||
|
(p) =>
|
||||||
|
p.name === 'num' ||
|
||||||
|
p.name === 'curr' ||
|
||||||
|
p.name === 'i' ||
|
||||||
|
p.name === 'j'
|
||||||
|
)
|
||||||
|
: dataState.pointers;
|
||||||
|
|
||||||
|
const prefixPointers = prefixArray
|
||||||
|
? dataState.pointers.filter(
|
||||||
|
(p) =>
|
||||||
|
p.name === 'prefix[i]' ||
|
||||||
|
p.name === 'prefix[j+1]' ||
|
||||||
|
(p.name !== 'num' &&
|
||||||
|
p.name !== 'curr' &&
|
||||||
|
p.name !== 'i' &&
|
||||||
|
p.name !== 'j')
|
||||||
|
)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
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 gap-4">
|
||||||
|
{/* Fixed height area for calculation bubble */}
|
||||||
|
<div className="flex h-8 items-center justify-center">
|
||||||
|
<CalculationBubble calculation={calculation} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Original array */}
|
||||||
|
{originalArray && (
|
||||||
|
<ArrayView
|
||||||
|
array={originalArray}
|
||||||
|
pointers={originalPointers}
|
||||||
|
elementSize="sm"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Prefix sum array (appears during execution) */}
|
||||||
|
{prefixArray && (
|
||||||
|
<ArrayView
|
||||||
|
array={prefixArray}
|
||||||
|
pointers={prefixPointers}
|
||||||
|
elementSize="sm"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</VisualizationContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { useVisualization } from "@/lib/visualizations/use-visualization";
|
import { useVisualization } from '@/lib/visualizations/use-visualization';
|
||||||
import type { AlgorithmDefinition } from "@/lib/visualizations/types";
|
import type { AlgorithmDefinition } from '@/lib/visualizations/types';
|
||||||
import { VisualizationContainer } from "../core/visualization-container";
|
import { VisualizationContainer } from '../core/visualization-container';
|
||||||
import { ArrayView } from "../data-structures/array-view";
|
import { ArrayView } from '../data-structures/array-view';
|
||||||
import { CalculationBubble } from "../primitives/calculation-bubble";
|
import { CalculationBubble } from '../primitives/calculation-bubble';
|
||||||
|
|
||||||
interface TwoPointersVisualizationProps {
|
interface TwoPointersVisualizationProps {
|
||||||
algorithm: AlgorithmDefinition;
|
algorithm: AlgorithmDefinition;
|
||||||
@@ -59,7 +59,7 @@ export function TwoPointersVisualization({
|
|||||||
<ArrayView
|
<ArrayView
|
||||||
array={mainArray}
|
array={mainArray}
|
||||||
pointers={dataState.pointers}
|
pointers={dataState.pointers}
|
||||||
elementSize="md"
|
elementSize="sm"
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { AnimatePresence, motion } from "framer-motion";
|
import { AnimatePresence, motion } from 'framer-motion';
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from '@/lib/utils';
|
||||||
import type { DecisionCallout, VisualizationPhase } from "@/lib/visualizations/types";
|
import type { DecisionCallout, VisualizationPhase } from '@/lib/visualizations/types';
|
||||||
|
|
||||||
interface ExplanationPanelProps {
|
interface ExplanationPanelProps {
|
||||||
explanation: string;
|
explanation: string;
|
||||||
@@ -12,19 +12,19 @@ interface ExplanationPanelProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const PHASE_LABELS: Record<VisualizationPhase, string> = {
|
const PHASE_LABELS: Record<VisualizationPhase, string> = {
|
||||||
problem: "Problem",
|
problem: 'Problem',
|
||||||
intuition: "Intuition",
|
intuition: 'Intuition',
|
||||||
pattern: "Pattern",
|
pattern: 'Pattern',
|
||||||
code: "Code",
|
code: 'Code',
|
||||||
execution: "Execution",
|
execution: 'Execution',
|
||||||
};
|
};
|
||||||
|
|
||||||
const PHASE_COLORS: Record<VisualizationPhase, string> = {
|
const PHASE_COLORS: Record<VisualizationPhase, string> = {
|
||||||
problem: "bg-red-500/20 text-red-500",
|
problem: 'bg-error/20 text-error',
|
||||||
intuition: "bg-amber-500/20 text-amber-500",
|
intuition: 'bg-warning/20 text-warning',
|
||||||
pattern: "bg-blue-500/20 text-blue-500",
|
pattern: 'bg-info/20 text-info',
|
||||||
code: "bg-[var(--primary)]/20 text-[var(--primary)]",
|
code: 'bg-primary/20 text-primary',
|
||||||
execution: "bg-green-500/20 text-green-500",
|
execution: 'bg-success/20 text-success',
|
||||||
};
|
};
|
||||||
|
|
||||||
export function ExplanationPanel({
|
export function ExplanationPanel({
|
||||||
@@ -36,14 +36,14 @@ export function ExplanationPanel({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"h-72 overflow-y-auto rounded-lg border border-[var(--border)] bg-[var(--card)] p-4",
|
'h-28 overflow-y-auto rounded-lg border border-border bg-surface p-4',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="mb-3 flex items-center gap-2">
|
<div className="mb-3 flex items-center gap-2">
|
||||||
<span
|
<span
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full px-2.5 py-0.5 text-xs font-medium",
|
'rounded-full px-2.5 py-0.5 text-xs font-medium',
|
||||||
PHASE_COLORS[phase]
|
PHASE_COLORS[phase]
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
@@ -59,7 +59,7 @@ export function ExplanationPanel({
|
|||||||
exit={{ opacity: 0, y: -10 }}
|
exit={{ opacity: 0, y: -10 }}
|
||||||
transition={{ duration: 0.2 }}
|
transition={{ duration: 0.2 }}
|
||||||
>
|
>
|
||||||
<p className="text-base leading-relaxed text-[var(--foreground)]">
|
<p className="text-base leading-relaxed text-foreground">
|
||||||
{explanation}
|
{explanation}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@@ -67,23 +67,23 @@ export function ExplanationPanel({
|
|||||||
<motion.div
|
<motion.div
|
||||||
initial={{ opacity: 0, scale: 0.95 }}
|
initial={{ opacity: 0, scale: 0.95 }}
|
||||||
animate={{ opacity: 1, scale: 1 }}
|
animate={{ opacity: 1, scale: 1 }}
|
||||||
className="mt-4 rounded-lg border border-[var(--primary)]/30 bg-[var(--primary)]/5 p-4"
|
className="mt-4 rounded-lg border border-primary/30 bg-primary/5 p-4"
|
||||||
>
|
>
|
||||||
<div className="mb-2 text-sm font-medium text-[var(--primary)]">
|
<div className="mb-2 text-sm font-medium text-primary">
|
||||||
Decision Point
|
Decision Point
|
||||||
</div>
|
</div>
|
||||||
<div className="space-y-2 text-sm">
|
<div className="space-y-2 text-sm">
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="font-medium text-[var(--muted-foreground)]">Q:</span>
|
<span className="font-medium text-foreground-muted">Q:</span>
|
||||||
<span className="text-[var(--foreground)]">{decision.question}</span>
|
<span className="text-foreground">{decision.question}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="font-medium text-[var(--muted-foreground)]">A:</span>
|
<span className="font-medium text-foreground-muted">A:</span>
|
||||||
<span className="font-mono text-[var(--primary)]">{decision.answer}</span>
|
<span className="font-mono text-primary">{decision.answer}</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex items-start gap-2">
|
<div className="flex items-start gap-2">
|
||||||
<span className="font-medium text-[var(--muted-foreground)]">→</span>
|
<span className="font-medium text-foreground-muted">→</span>
|
||||||
<span className="text-green-500">{decision.action}</span>
|
<span className="text-success">{decision.action}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from '@/lib/utils';
|
||||||
import type {
|
import type {
|
||||||
PlaybackSpeed,
|
PlaybackSpeed,
|
||||||
VisualizationControls,
|
VisualizationControls,
|
||||||
} from "@/lib/visualizations/types";
|
} from '@/lib/visualizations/types';
|
||||||
|
|
||||||
interface StepControlsProps {
|
interface StepControlsProps {
|
||||||
currentStepIndex: number;
|
currentStepIndex: number;
|
||||||
@@ -33,21 +33,20 @@ export function StepControls({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col gap-3 rounded-lg border border-[var(--border)] bg-[var(--card)] p-4",
|
'flex items-center gap-4 rounded-lg border border-border bg-surface px-4 py-2',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-4">
|
<div className="flex items-center gap-1">
|
||||||
<div className="flex items-center gap-2">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={controls.goToFirst}
|
onClick={controls.goToFirst}
|
||||||
disabled={isAtStart}
|
disabled={isAtStart}
|
||||||
aria-label="Go to first step"
|
aria-label="Go to first step"
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-md p-2 transition-colors",
|
'rounded-md p-1.5 transition-colors',
|
||||||
"hover:bg-[var(--muted)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]",
|
'hover:bg-surface-variant focus:outline-none focus:ring-2 focus:ring-primary',
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40"
|
'disabled:cursor-not-allowed disabled:opacity-40'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<FirstIcon />
|
<FirstIcon />
|
||||||
@@ -59,9 +58,9 @@ export function StepControls({
|
|||||||
disabled={isAtStart}
|
disabled={isAtStart}
|
||||||
aria-label="Previous step"
|
aria-label="Previous step"
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-md p-2 transition-colors",
|
'rounded-md p-1.5 transition-colors',
|
||||||
"hover:bg-[var(--muted)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]",
|
'hover:bg-surface-variant focus:outline-none focus:ring-2 focus:ring-primary',
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40"
|
'disabled:cursor-not-allowed disabled:opacity-40'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<PreviousIcon />
|
<PreviousIcon />
|
||||||
@@ -70,10 +69,10 @@ export function StepControls({
|
|||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={controls.togglePlay}
|
onClick={controls.togglePlay}
|
||||||
aria-label={isPlaying ? "Pause" : "Play"}
|
aria-label={isPlaying ? 'Pause' : 'Play'}
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-full bg-[var(--primary)] p-3 text-white transition-colors",
|
'rounded-full bg-primary p-2 text-white transition-colors',
|
||||||
"hover:opacity-90 focus:outline-none focus:ring-2 focus:ring-[var(--primary)] focus:ring-offset-2"
|
'hover:bg-primary-hover focus:outline-none focus:ring-2 focus:ring-primary focus:ring-offset-2 focus:ring-offset-surface'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
{isPlaying ? <PauseIcon /> : <PlayIcon />}
|
||||||
@@ -85,9 +84,9 @@ export function StepControls({
|
|||||||
disabled={isAtEnd}
|
disabled={isAtEnd}
|
||||||
aria-label="Next step"
|
aria-label="Next step"
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-md p-2 transition-colors",
|
'rounded-md p-1.5 transition-colors',
|
||||||
"hover:bg-[var(--muted)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]",
|
'hover:bg-surface-variant focus:outline-none focus:ring-2 focus:ring-primary',
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40"
|
'disabled:cursor-not-allowed disabled:opacity-40'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<NextIcon />
|
<NextIcon />
|
||||||
@@ -99,41 +98,16 @@ export function StepControls({
|
|||||||
disabled={isAtEnd}
|
disabled={isAtEnd}
|
||||||
aria-label="Go to last step"
|
aria-label="Go to last step"
|
||||||
className={cn(
|
className={cn(
|
||||||
"rounded-md p-2 transition-colors",
|
'rounded-md p-1.5 transition-colors',
|
||||||
"hover:bg-[var(--muted)] focus:outline-none focus:ring-2 focus:ring-[var(--primary)]",
|
'hover:bg-surface-variant focus:outline-none focus:ring-2 focus:ring-primary',
|
||||||
"disabled:cursor-not-allowed disabled:opacity-40"
|
'disabled:cursor-not-allowed disabled:opacity-40'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<LastIcon />
|
<LastIcon />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3">
|
<div className="relative h-2 flex-1 overflow-hidden rounded-full bg-surface-variant">
|
||||||
<span className="text-sm text-[var(--muted-foreground)]">
|
|
||||||
Step {currentStepIndex + 1} of {totalSteps}
|
|
||||||
</span>
|
|
||||||
|
|
||||||
<select
|
|
||||||
value={speed}
|
|
||||||
onChange={(e) =>
|
|
||||||
controls.setSpeed(parseFloat(e.target.value) as PlaybackSpeed)
|
|
||||||
}
|
|
||||||
aria-label="Playback speed"
|
|
||||||
className={cn(
|
|
||||||
"rounded-md border border-[var(--border)] bg-[var(--card)] px-2 py-1 text-sm",
|
|
||||||
"focus:border-[var(--primary)] focus:outline-none focus:ring-1 focus:ring-[var(--primary)]"
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{SPEED_OPTIONS.map((s) => (
|
|
||||||
<option key={s} value={s}>
|
|
||||||
{s}x
|
|
||||||
</option>
|
|
||||||
))}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="relative h-2 w-full overflow-hidden rounded-full bg-[var(--muted)]">
|
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="absolute inset-0 w-full cursor-pointer"
|
className="absolute inset-0 w-full cursor-pointer"
|
||||||
@@ -147,11 +121,35 @@ export function StepControls({
|
|||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="h-full bg-[var(--primary)] transition-all duration-200"
|
className="h-full bg-primary transition-all duration-200"
|
||||||
style={{ width: `${progress}%` }}
|
style={{ width: `${progress}%` }}
|
||||||
/>
|
/>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<span className="whitespace-nowrap text-xs text-foreground-muted">
|
||||||
|
{currentStepIndex + 1}/{totalSteps}
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<select
|
||||||
|
value={speed}
|
||||||
|
onChange={(e) =>
|
||||||
|
controls.setSpeed(parseFloat(e.target.value) as PlaybackSpeed)
|
||||||
|
}
|
||||||
|
aria-label="Playback speed"
|
||||||
|
className={cn(
|
||||||
|
'rounded-md border border-border bg-surface px-1.5 py-0.5 text-xs',
|
||||||
|
'focus:border-primary focus:outline-none focus:ring-1 focus:ring-primary'
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{SPEED_OPTIONS.map((s) => (
|
||||||
|
<option key={s} value={s}>
|
||||||
|
{s}x
|
||||||
|
</option>
|
||||||
|
))}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from '@/lib/utils';
|
||||||
import type { ReactNode } from "react";
|
import type { ReactNode } from 'react';
|
||||||
import type {
|
import type {
|
||||||
AlgorithmCode,
|
AlgorithmCode,
|
||||||
AlgorithmPattern,
|
AlgorithmPattern,
|
||||||
@@ -10,11 +10,11 @@ import type {
|
|||||||
VariableState,
|
VariableState,
|
||||||
VisualizationControls,
|
VisualizationControls,
|
||||||
VisualizationPhase,
|
VisualizationPhase,
|
||||||
} from "@/lib/visualizations/types";
|
} from '@/lib/visualizations/types';
|
||||||
import { CodePanel } from "./code-panel";
|
import { CodePanel } from './code-panel';
|
||||||
import { ExplanationPanel } from "./explanation-panel";
|
import { ExplanationPanel } from './explanation-panel';
|
||||||
import { StepControls } from "./step-controls";
|
import { StepControls } from './step-controls';
|
||||||
import { VariableInspector } from "./variable-inspector";
|
import { VariableInspector } from './variable-inspector';
|
||||||
|
|
||||||
interface VisualizationContainerProps {
|
interface VisualizationContainerProps {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -58,43 +58,43 @@ export function VisualizationContainer({
|
|||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col gap-4 rounded-xl border border-[var(--border)] bg-[var(--card)] p-4",
|
'flex flex-col rounded-xl border border-border bg-background-subtle p-4',
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<header className="flex flex-wrap items-center justify-between gap-2">
|
<header className="mb-3 flex flex-wrap items-center justify-between gap-2">
|
||||||
<h2 className="text-xl font-semibold text-[var(--foreground)]">{title}</h2>
|
<h2 className="text-xl font-semibold text-foreground">{title}</h2>
|
||||||
<span className="rounded-full bg-[var(--primary)]/20 px-3 py-1 text-sm font-medium text-[var(--primary)]">
|
<span className="rounded-full bg-primary/20 px-3 py-1 text-sm font-medium text-primary">
|
||||||
{pattern.name}
|
{pattern.name}
|
||||||
</span>
|
</span>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className="grid gap-4 lg:grid-cols-[1fr_1.5fr]">
|
<div className="grid gap-3 lg:grid-cols-[1fr_1.5fr]">
|
||||||
<div className="flex flex-col gap-4">
|
<div className="flex flex-col gap-3">
|
||||||
<CodePanel
|
<CodePanel
|
||||||
code={code}
|
code={code}
|
||||||
currentLine={currentLine}
|
currentLine={currentLine}
|
||||||
highlightLines={highlightLines}
|
highlightLines={highlightLines}
|
||||||
className="max-h-80 min-h-48"
|
className="h-64"
|
||||||
/>
|
/>
|
||||||
<VariableInspector variables={variables} />
|
<VariableInspector variables={variables} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex flex-col gap-4">
|
|
||||||
<div
|
<div
|
||||||
className="flex h-64 items-center justify-center rounded-lg border border-[var(--border)] bg-[var(--background)] p-4"
|
className="flex min-h-80 items-center justify-center 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"
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<ExplanationPanel
|
<ExplanationPanel
|
||||||
explanation={explanation}
|
explanation={explanation}
|
||||||
decision={decision}
|
decision={decision}
|
||||||
phase={phase}
|
phase={phase}
|
||||||
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<StepControls
|
<StepControls
|
||||||
currentStepIndex={currentStepIndex}
|
currentStepIndex={currentStepIndex}
|
||||||
@@ -103,6 +103,7 @@ export function VisualizationContainer({
|
|||||||
speed={speed}
|
speed={speed}
|
||||||
controls={controls}
|
controls={controls}
|
||||||
progress={progress}
|
progress={progress}
|
||||||
|
className="mt-3"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -14,4 +14,5 @@ export { CalculationBubble } from "./primitives/calculation-bubble";
|
|||||||
export { ArrayView } from "./data-structures/array-view";
|
export { ArrayView } from "./data-structures/array-view";
|
||||||
|
|
||||||
// Algorithm visualizations
|
// Algorithm visualizations
|
||||||
|
export { PrefixSumVisualization } from "./algorithms/prefix-sum";
|
||||||
export { TwoPointersVisualization } from "./algorithms/two-pointers";
|
export { TwoPointersVisualization } from "./algorithms/two-pointers";
|
||||||
|
|||||||
773
frontend/src/content/algorithms/binary-search.ts
Normal file
773
frontend/src/content/algorithms/binary-search.ts
Normal file
@@ -0,0 +1,773 @@
|
|||||||
|
import type { AlgorithmDefinition } from '@/lib/visualizations/types';
|
||||||
|
|
||||||
|
export const binarySearchAlgorithm: AlgorithmDefinition = {
|
||||||
|
id: 'binary-search',
|
||||||
|
title: 'Binary Search',
|
||||||
|
slug: 'binary-search',
|
||||||
|
pattern: {
|
||||||
|
name: 'Binary Search',
|
||||||
|
description:
|
||||||
|
'Repeatedly divide the search space in half by comparing to the middle element.',
|
||||||
|
},
|
||||||
|
problemStatement:
|
||||||
|
'Given a sorted array of integers, find the index of a target value. Return -1 if not found.',
|
||||||
|
intuition:
|
||||||
|
'In a sorted array, we can eliminate half of the remaining elements with each comparison. Compare the target to the middle element: if smaller, search left half; if larger, search right half.',
|
||||||
|
code: {
|
||||||
|
language: 'python',
|
||||||
|
code: `def binary_search(nums: list[int], target: int) -> int:
|
||||||
|
left = 0
|
||||||
|
right = len(nums) - 1
|
||||||
|
|
||||||
|
while left <= right:
|
||||||
|
mid = (left + right) // 2
|
||||||
|
|
||||||
|
if nums[mid] == target:
|
||||||
|
return mid
|
||||||
|
elif nums[mid] < target:
|
||||||
|
left = mid + 1
|
||||||
|
else:
|
||||||
|
right = mid - 1
|
||||||
|
|
||||||
|
return -1`,
|
||||||
|
},
|
||||||
|
initialExample: {
|
||||||
|
input: { nums: [1, 3, 5, 7, 9, 11, 13], target: 9 },
|
||||||
|
expected: 4,
|
||||||
|
},
|
||||||
|
steps: [
|
||||||
|
// Phase 1: Problem (2 steps)
|
||||||
|
{
|
||||||
|
id: 'problem-1',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'We have a sorted array [1, 3, 5, 7, 9, 11, 13] and need to find the index of target value 9.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'problem-2',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'A linear search would check each element, giving O(n) time. Can we use the sorted property to do better?',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'comparing' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 2: Intuition (3 steps)
|
||||||
|
{
|
||||||
|
id: 'intuition-1',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Key insight: If we check the middle element, we can eliminate half the array with one comparison.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'mid', name: 'mid', index: 3, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-2',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Middle element is 7. Since 9 > 7, the target must be in the right half. We can ignore the left half entirely!',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-3',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Repeat this process on the remaining half. Each step halves the search space, giving O(log n) time complexity.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'highlighted' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'mid', name: 'mid', index: 5, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 3: Code walkthrough (5 steps)
|
||||||
|
{
|
||||||
|
id: 'code-1',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Initialize two pointers: left at 0, right at the last index.',
|
||||||
|
codeLine: 2,
|
||||||
|
codeHighlightLines: [2, 3],
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-2',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Loop while the search space is valid (left <= right).',
|
||||||
|
codeLine: 5,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-3',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Calculate the middle index: mid = (left + right) // 2.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-4',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'If nums[mid] equals target, we found it! Otherwise, narrow the search space.',
|
||||||
|
codeLine: 8,
|
||||||
|
codeHighlightLines: [8, 9, 10, 11, 12, 13],
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-5',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'If target not found after search space is exhausted, return -1.',
|
||||||
|
codeLine: 15,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'target', name: 'target', value: 9 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 4: Execution (~15 steps)
|
||||||
|
{
|
||||||
|
id: 'exec-1',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Initialize left = 0, right = 6 (len(nums) - 1).',
|
||||||
|
codeLine: 2,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 0 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-2',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Check loop condition: left (0) <= right (6)? Yes, enter loop.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is left <= right?',
|
||||||
|
answer: '0 <= 6 = true',
|
||||||
|
action: 'Enter the while loop',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 0 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-3',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Calculate mid = (0 + 6) // 2 = 3. Middle element is 7.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'comparing' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left', showValue: true },
|
||||||
|
{ id: 'mid', name: 'mid', index: 3, color: 'mid', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 0 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 3, derivation: '(0 + 6) // 2' },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '(0 + 6) // 2', result: '3', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-4',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: nums[3] = 7 vs target = 9. 7 < 9, so target is in right half.',
|
||||||
|
codeLine: 10,
|
||||||
|
decision: {
|
||||||
|
question: 'Is nums[mid] == target?',
|
||||||
|
answer: '7 != 9, and 7 < 9',
|
||||||
|
action: 'Search the right half (left = mid + 1)',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 7, index: 3, state: 'comparing' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left', showValue: true },
|
||||||
|
{ id: 'mid', name: 'mid', index: 3, color: 'mid', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 0 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 3 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '7 < 9', result: 'true', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-5',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Update left = mid + 1 = 4. The left half is eliminated.',
|
||||||
|
codeLine: 11,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4, previousValue: 0 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-6',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Check loop condition: left (4) <= right (6)? Yes, continue.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is left <= right?',
|
||||||
|
answer: '4 <= 6 = true',
|
||||||
|
action: 'Continue the while loop',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'normal' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-7',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Calculate mid = (4 + 6) // 2 = 5. Middle element is 11.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'comparing' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'mid', name: 'mid', index: 5, color: 'mid', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 5, previousValue: 3, derivation: '(4 + 6) // 2' },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-2', expression: '(4 + 6) // 2', result: '5', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-8',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: nums[5] = 11 vs target = 9. 11 > 9, so target is in left half.',
|
||||||
|
codeLine: 12,
|
||||||
|
decision: {
|
||||||
|
question: 'Is nums[mid] == target?',
|
||||||
|
answer: '11 != 9, and 11 > 9',
|
||||||
|
action: 'Search the left half (right = mid - 1)',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'comparing' },
|
||||||
|
{ value: 13, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'mid', name: 'mid', index: 5, color: 'mid', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 6, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 6 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 5 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-2', expression: '11 > 9', result: 'true', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-9',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Update right = mid - 1 = 4. The right portion is eliminated.',
|
||||||
|
codeLine: 13,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'dimmed' },
|
||||||
|
{ value: 13, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 4, previousValue: 6 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-10',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Check loop condition: left (4) <= right (4)? Yes, continue.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is left <= right?',
|
||||||
|
answer: '4 <= 4 = true',
|
||||||
|
action: 'Continue the while loop',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 11, index: 5, state: 'dimmed' },
|
||||||
|
{ value: 13, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-11',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Calculate mid = (4 + 4) // 2 = 4. Middle element is 9.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'comparing' },
|
||||||
|
{ value: 11, index: 5, state: 'dimmed' },
|
||||||
|
{ value: 13, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 4, color: 'left', showValue: true },
|
||||||
|
{ id: 'mid', name: 'mid', index: 4, color: 'mid', showValue: true },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'left', name: 'left', value: 4 },
|
||||||
|
{ id: 'right', name: 'right', value: 4 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 4, previousValue: 5, derivation: '(4 + 4) // 2' },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-3', expression: '(4 + 4) // 2', result: '4', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-12',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: nums[4] = 9 vs target = 9. Found it!',
|
||||||
|
codeLine: 8,
|
||||||
|
decision: {
|
||||||
|
question: 'Is nums[mid] == target?',
|
||||||
|
answer: '9 == 9 = true',
|
||||||
|
action: 'Return mid = 4',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'success' },
|
||||||
|
{ value: 11, index: 5, state: 'dimmed' },
|
||||||
|
{ value: 13, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'mid', name: 'mid', index: 4, color: 'mid', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'mid', name: 'mid', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-3', expression: '9 == 9', result: 'true', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-13',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation:
|
||||||
|
'Return 4. Found target 9 at index 4 in just 3 comparisons! O(log n) time complexity.',
|
||||||
|
codeLine: 9,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 1, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 7, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'success' },
|
||||||
|
{ value: 11, index: 5, state: 'dimmed' },
|
||||||
|
{ value: 13, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'mid', name: 'mid', index: 4, color: 'mid', showValue: true },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'target', name: 'target', value: 9 },
|
||||||
|
{ id: 'result', name: 'result', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
807
frontend/src/content/algorithms/prefix-sum.ts
Normal file
807
frontend/src/content/algorithms/prefix-sum.ts
Normal file
@@ -0,0 +1,807 @@
|
|||||||
|
import type { AlgorithmDefinition } from '@/lib/visualizations/types';
|
||||||
|
|
||||||
|
export const prefixSumAlgorithm: AlgorithmDefinition = {
|
||||||
|
id: 'prefix-sum',
|
||||||
|
title: 'Prefix Sum - Range Query',
|
||||||
|
slug: 'prefix-sum',
|
||||||
|
pattern: {
|
||||||
|
name: 'Prefix Sum',
|
||||||
|
description:
|
||||||
|
'Precompute cumulative sums to answer range sum queries in O(1) time.',
|
||||||
|
},
|
||||||
|
problemStatement:
|
||||||
|
'Given an array, answer multiple range sum queries efficiently. Each query asks for the sum of elements from index i to j.',
|
||||||
|
intuition:
|
||||||
|
'Build a prefix sum array where prefix[k] = sum of all elements from 0 to k-1. Then any range sum from i to j is simply prefix[j+1] - prefix[i].',
|
||||||
|
code: {
|
||||||
|
language: 'python',
|
||||||
|
code: `def build_prefix(nums: list[int]) -> list[int]:
|
||||||
|
prefix = [0]
|
||||||
|
for num in nums:
|
||||||
|
prefix.append(prefix[-1] + num)
|
||||||
|
return prefix
|
||||||
|
|
||||||
|
def range_sum(prefix: list[int], i: int, j: int) -> int:
|
||||||
|
return prefix[j + 1] - prefix[i]`,
|
||||||
|
},
|
||||||
|
initialExample: {
|
||||||
|
input: { nums: [3, 1, 4, 1, 5, 9], query: { i: 1, j: 4 } },
|
||||||
|
expected: 11,
|
||||||
|
},
|
||||||
|
steps: [
|
||||||
|
// Phase 1: Problem (2 steps)
|
||||||
|
{
|
||||||
|
id: 'problem-1',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'We have an array [3, 1, 4, 1, 5, 9] and need to efficiently answer range sum queries like "sum from index 1 to 4".',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'problem-2',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'Naively summing each range takes O(n) per query. With many queries, this becomes expensive. Can we precompute something?',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'i', name: 'i', index: 1, color: 'left' },
|
||||||
|
{ id: 'j', name: 'j', index: 4, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'i', name: 'i', value: 1 },
|
||||||
|
{ id: 'j', name: 'j', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 2: Intuition (3 steps)
|
||||||
|
{
|
||||||
|
id: 'intuition-1',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Key insight: Build a prefix sum array where each element is the cumulative sum of all elements up to that point.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'normal' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-2',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'prefix[k] represents the sum of elements from index 0 to k-1. The extra 0 at the start simplifies our formula.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'normal' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '3 + 1 + 4', result: '8', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-3',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Range sum from i to j = prefix[j+1] - prefix[i]. This is O(1) per query after O(n) preprocessing!',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'comparing' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'comparing' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'i', name: 'i', index: 1, color: 'left' },
|
||||||
|
{ id: 'j+1', name: 'j+1', index: 5, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'i', name: 'i', value: 1 },
|
||||||
|
{ id: 'j', name: 'j', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '14 - 3', result: '11', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 3: Code walkthrough (4 steps)
|
||||||
|
{
|
||||||
|
id: 'code-1',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Start with prefix = [0]. This handles the edge case of summing from index 0.',
|
||||||
|
codeLine: 2,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-2',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'For each element, append prefix[-1] + num. This builds the cumulative sum.',
|
||||||
|
codeLine: 3,
|
||||||
|
codeHighlightLines: [3, 4],
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-3',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'The range_sum function computes prefix[j+1] - prefix[i] in O(1).',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-4',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Why j+1? Because prefix[k] is the sum of elements 0 through k-1. So prefix[j+1] includes element j.',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 4: Execution - Building prefix array (10 steps)
|
||||||
|
{
|
||||||
|
id: 'exec-1',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Initialize prefix = [0].',
|
||||||
|
codeLine: 2,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-2',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[0] = 3. Append prefix[-1] + 3 = 0 + 3 = 3.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'comparing' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 0, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '0 + 3', result: '3', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-3',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[1] = 1. Append prefix[-1] + 1 = 3 + 1 = 4.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'comparing' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 1, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-2', expression: '3 + 1', result: '4', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-4',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[2] = 4. Append prefix[-1] + 4 = 4 + 4 = 8.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 4, index: 2, state: 'comparing' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 2, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-3', expression: '4 + 4', result: '8', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-5',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[3] = 1. Append prefix[-1] + 1 = 8 + 1 = 9.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 4, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'comparing' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 3, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-4', expression: '8 + 1', result: '9', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-6',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[4] = 5. Append prefix[-1] + 5 = 9 + 5 = 14.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 4, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 4, state: 'comparing' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 4, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-5', expression: '9 + 5', result: '14', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-7',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Process nums[5] = 9. Append prefix[-1] + 9 = 14 + 9 = 23.',
|
||||||
|
codeLine: 4,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 4, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 4, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 5, state: 'comparing' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'normal' },
|
||||||
|
{ value: 23, index: 6, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'curr', name: 'num', index: 5, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-6', expression: '14 + 9', result: '23', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-8',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Prefix array complete! Now we can answer range queries in O(1).',
|
||||||
|
codeLine: 5,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 5, index: 4, state: 'normal' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'success' },
|
||||||
|
{ value: 3, index: 1, state: 'success' },
|
||||||
|
{ value: 4, index: 2, state: 'success' },
|
||||||
|
{ value: 8, index: 3, state: 'success' },
|
||||||
|
{ value: 9, index: 4, state: 'success' },
|
||||||
|
{ value: 14, index: 5, state: 'success' },
|
||||||
|
{ value: 23, index: 6, state: 'success' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// Range query execution
|
||||||
|
{
|
||||||
|
id: 'exec-9',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Query: sum from index 1 to 4. We need elements [1, 4, 1, 5].',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'normal' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'normal' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'i', name: 'i', index: 1, color: 'left' },
|
||||||
|
{ id: 'j', name: 'j', index: 4, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'i', name: 'i', value: 1 },
|
||||||
|
{ id: 'j', name: 'j', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-10',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Use formula: prefix[j+1] - prefix[i] = prefix[5] - prefix[1].',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 4, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'comparing' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'comparing' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'prefix[i]', name: 'prefix[i]', index: 1, color: 'left' },
|
||||||
|
{ id: 'prefix[j+1]', name: 'prefix[j+1]', index: 5, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'i', name: 'i', value: 1 },
|
||||||
|
{ id: 'j', name: 'j', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-11',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Calculate: prefix[5] - prefix[1] = 14 - 3 = 11.',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'success' },
|
||||||
|
{ value: 4, index: 2, state: 'success' },
|
||||||
|
{ value: 1, index: 3, state: 'success' },
|
||||||
|
{ value: 5, index: 4, state: 'success' },
|
||||||
|
{ value: 9, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'normal' },
|
||||||
|
{ value: 3, index: 1, state: 'comparing' },
|
||||||
|
{ value: 4, index: 2, state: 'normal' },
|
||||||
|
{ value: 8, index: 3, state: 'normal' },
|
||||||
|
{ value: 9, index: 4, state: 'normal' },
|
||||||
|
{ value: 14, index: 5, state: 'comparing' },
|
||||||
|
{ value: 23, index: 6, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [
|
||||||
|
{ id: 'i', name: 'i', value: 1 },
|
||||||
|
{ id: 'j', name: 'j', value: 4 },
|
||||||
|
{ id: 'result', name: 'result', value: 11, derivation: '14 - 3' },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-7', expression: '14 - 3', result: '11', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-12',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Verify: 1 + 4 + 1 + 5 = 11. Correct! O(n) preprocessing enables O(1) queries.',
|
||||||
|
codeLine: 8,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
label: 'Original Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 3, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'success' },
|
||||||
|
{ value: 4, index: 2, state: 'success' },
|
||||||
|
{ value: 1, index: 3, state: 'success' },
|
||||||
|
{ value: 5, index: 4, state: 'success' },
|
||||||
|
{ value: 9, index: 5, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'prefix',
|
||||||
|
label: 'Prefix Sum Array',
|
||||||
|
elements: [
|
||||||
|
{ value: 0, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 3, index: 1, state: 'success' },
|
||||||
|
{ value: 4, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 8, index: 3, state: 'dimmed' },
|
||||||
|
{ value: 9, index: 4, state: 'dimmed' },
|
||||||
|
{ value: 14, index: 5, state: 'success' },
|
||||||
|
{ value: 23, index: 6, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [
|
||||||
|
{ id: 'result', name: 'result', value: 11 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-8', expression: '1 + 4 + 1 + 5', result: '11', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
732
frontend/src/content/algorithms/sliding-window.ts
Normal file
732
frontend/src/content/algorithms/sliding-window.ts
Normal file
@@ -0,0 +1,732 @@
|
|||||||
|
import type { AlgorithmDefinition } from '@/lib/visualizations/types';
|
||||||
|
|
||||||
|
export const slidingWindowAlgorithm: AlgorithmDefinition = {
|
||||||
|
id: 'max-sum-subarray',
|
||||||
|
title: 'Maximum Sum Subarray of Size K',
|
||||||
|
slug: 'max-sum-subarray',
|
||||||
|
pattern: {
|
||||||
|
name: 'Sliding Window',
|
||||||
|
description:
|
||||||
|
'Maintain a window of elements that slides through the array, updating state incrementally.',
|
||||||
|
},
|
||||||
|
problemStatement:
|
||||||
|
'Given an array of integers and a window size k, find the maximum sum of any contiguous subarray of size k.',
|
||||||
|
intuition:
|
||||||
|
'Instead of recalculating the sum for each window from scratch (O(n*k)), we can slide the window by subtracting the element leaving and adding the element entering (O(n)).',
|
||||||
|
code: {
|
||||||
|
language: 'python',
|
||||||
|
code: `def max_sum_subarray(nums: list[int], k: int) -> int:
|
||||||
|
window_sum = sum(nums[:k])
|
||||||
|
max_sum = window_sum
|
||||||
|
|
||||||
|
for i in range(k, len(nums)):
|
||||||
|
window_sum += nums[i] - nums[i - k]
|
||||||
|
max_sum = max(max_sum, window_sum)
|
||||||
|
|
||||||
|
return max_sum`,
|
||||||
|
},
|
||||||
|
initialExample: {
|
||||||
|
input: { nums: [2, 1, 5, 1, 3, 2], k: 3 },
|
||||||
|
expected: 9,
|
||||||
|
},
|
||||||
|
steps: [
|
||||||
|
// Phase 1: Problem (2 steps)
|
||||||
|
{
|
||||||
|
id: 'problem-1',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'We have an array [2, 1, 5, 1, 3, 2] and need to find the maximum sum of any 3 consecutive elements.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'problem-2',
|
||||||
|
phase: 'problem',
|
||||||
|
explanation:
|
||||||
|
'A brute force approach would sum each window separately, giving O(n*k) time. Can we avoid redundant calculations?',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 2: Intuition (3 steps)
|
||||||
|
{
|
||||||
|
id: 'intuition-1',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Key insight: When sliding the window one position right, we only need to subtract the leaving element and add the entering element.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 2, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-2',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Window slides: element at left exits (subtract), element after right enters (add). This gives O(1) per slide!',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 1, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 3, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'intuition-3',
|
||||||
|
phase: 'intuition',
|
||||||
|
explanation:
|
||||||
|
'Track the maximum sum seen so far as the window slides through the entire array.',
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: '?' },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 3: Code walkthrough (4 steps)
|
||||||
|
{
|
||||||
|
id: 'code-1',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'First, calculate the sum of the initial window (first k elements).',
|
||||||
|
codeLine: 2,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-2',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Initialize max_sum with the initial window sum. This is our best answer so far.',
|
||||||
|
codeLine: 3,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-3',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Slide the window: add the new element nums[i], subtract the leaving element nums[i-k].',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'code-4',
|
||||||
|
phase: 'code',
|
||||||
|
explanation:
|
||||||
|
'Update max_sum if the current window sum is larger.',
|
||||||
|
codeLine: 7,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'normal' },
|
||||||
|
{ value: 1, index: 1, state: 'normal' },
|
||||||
|
{ value: 5, index: 2, state: 'normal' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [{ id: 'k', name: 'k', value: 3 }],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// Phase 4: Execution (~15 steps)
|
||||||
|
{
|
||||||
|
id: 'exec-1',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation:
|
||||||
|
'Calculate the initial window sum: nums[0] + nums[1] + nums[2] = 2 + 1 + 5 = 8.',
|
||||||
|
codeLine: 2,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 2, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 8, derivation: '2 + 1 + 5' },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-1', expression: '2 + 1 + 5', result: '8', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-2',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Initialize max_sum = window_sum = 8.',
|
||||||
|
codeLine: 3,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 2, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 8 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-3',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Start sliding: i = 3 (k). Loop condition: i < len(nums) = 6? Yes.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is i < len(nums)?',
|
||||||
|
answer: '3 < 6 = true',
|
||||||
|
action: 'Enter the for loop with i = 3',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'comparing' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 0, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 2, color: 'right' },
|
||||||
|
{ id: 'i', name: 'i', index: 3, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 8 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 3 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-4',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Slide window: add nums[3]=1, subtract nums[0]=2. New sum: 8 + 1 - 2 = 7.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 1, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 3, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 7, previousValue: 8, derivation: '8 + 1 - 2' },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 3 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-2', expression: '8 + 1 - 2', result: '7', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-5',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: window_sum (7) vs max_sum (8). 7 < 8, so max_sum stays 8.',
|
||||||
|
codeLine: 7,
|
||||||
|
decision: {
|
||||||
|
question: 'Is window_sum > max_sum?',
|
||||||
|
answer: '7 > 8 = false',
|
||||||
|
action: 'Keep max_sum = 8',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 1, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 3, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 7 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 3 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-2', expression: '7 > 8', result: 'false', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-6',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Next iteration: i = 4. Loop condition: 4 < 6? Yes.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is i < len(nums)?',
|
||||||
|
answer: '4 < 6 = true',
|
||||||
|
action: 'Continue for loop with i = 4',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'highlighted' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'comparing' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 1, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 3, color: 'right' },
|
||||||
|
{ id: 'i', name: 'i', index: 4, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 7 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 4, previousValue: 3 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-7',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Slide window: add nums[4]=3, subtract nums[1]=1. New sum: 7 + 3 - 1 = 9.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 2, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 9, previousValue: 7, derivation: '7 + 3 - 1' },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-3', expression: '7 + 3 - 1', result: '9', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-8',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: window_sum (9) > max_sum (8)! Update max_sum to 9.',
|
||||||
|
codeLine: 7,
|
||||||
|
decision: {
|
||||||
|
question: 'Is window_sum > max_sum?',
|
||||||
|
answer: '9 > 8 = true',
|
||||||
|
action: 'Update max_sum = 9',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'success' },
|
||||||
|
{ value: 1, index: 3, state: 'success' },
|
||||||
|
{ value: 3, index: 4, state: 'success' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 2, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 9 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 9, previousValue: 8 },
|
||||||
|
{ id: 'i', name: 'i', value: 4 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-3', expression: '9 > 8', result: 'true', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-9',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Next iteration: i = 5. Loop condition: 5 < 6? Yes.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is i < len(nums)?',
|
||||||
|
answer: '5 < 6 = true',
|
||||||
|
action: 'Continue for loop with i = 5',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'highlighted' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 2, index: 5, state: 'comparing' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 2, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right' },
|
||||||
|
{ id: 'i', name: 'i', index: 5, color: 'mid' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 9 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 9 },
|
||||||
|
{ id: 'i', name: 'i', value: 5, previousValue: 4 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-10',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Slide window: add nums[5]=2, subtract nums[2]=5. New sum: 9 + 2 - 5 = 6.',
|
||||||
|
codeLine: 6,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 2, index: 5, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 3, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 5, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 6, previousValue: 9, derivation: '9 + 2 - 5' },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 9 },
|
||||||
|
{ id: 'i', name: 'i', value: 5 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-4', expression: '9 + 2 - 5', result: '6', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-11',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Compare: window_sum (6) vs max_sum (9). 6 < 9, so max_sum stays 9.',
|
||||||
|
codeLine: 7,
|
||||||
|
decision: {
|
||||||
|
question: 'Is window_sum > max_sum?',
|
||||||
|
answer: '6 > 9 = false',
|
||||||
|
action: 'Keep max_sum = 9',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'highlighted' },
|
||||||
|
{ value: 3, index: 4, state: 'highlighted' },
|
||||||
|
{ value: 2, index: 5, state: 'highlighted' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 3, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 5, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 6 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 9 },
|
||||||
|
{ id: 'i', name: 'i', value: 5 },
|
||||||
|
],
|
||||||
|
calculations: [
|
||||||
|
{ id: 'calc-4', expression: '6 > 9', result: 'false', position: 'above' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-12',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation: 'Next iteration: i = 6. Loop condition: 6 < 6? No, exit loop.',
|
||||||
|
codeLine: 5,
|
||||||
|
decision: {
|
||||||
|
question: 'Is i < len(nums)?',
|
||||||
|
answer: '6 < 6 = false',
|
||||||
|
action: 'Exit the for loop',
|
||||||
|
},
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 3, state: 'normal' },
|
||||||
|
{ value: 3, index: 4, state: 'normal' },
|
||||||
|
{ value: 2, index: 5, state: 'normal' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'window_sum', name: 'window_sum', value: 6 },
|
||||||
|
{ id: 'max_sum', name: 'max_sum', value: 9 },
|
||||||
|
{ id: 'i', name: 'i', value: 6, previousValue: 5 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'exec-13',
|
||||||
|
phase: 'execution',
|
||||||
|
explanation:
|
||||||
|
'Return max_sum = 9. The maximum sum window is [5, 1, 3] at indices 2-4. Achieved in O(n) time!',
|
||||||
|
codeLine: 9,
|
||||||
|
dataState: {
|
||||||
|
arrays: [
|
||||||
|
{
|
||||||
|
id: 'nums',
|
||||||
|
elements: [
|
||||||
|
{ value: 2, index: 0, state: 'dimmed' },
|
||||||
|
{ value: 1, index: 1, state: 'dimmed' },
|
||||||
|
{ value: 5, index: 2, state: 'success' },
|
||||||
|
{ value: 1, index: 3, state: 'success' },
|
||||||
|
{ value: 3, index: 4, state: 'success' },
|
||||||
|
{ value: 2, index: 5, state: 'dimmed' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
pointers: [
|
||||||
|
{ id: 'left', name: 'left', index: 2, color: 'left' },
|
||||||
|
{ id: 'right', name: 'right', index: 4, color: 'right' },
|
||||||
|
],
|
||||||
|
variables: [
|
||||||
|
{ id: 'k', name: 'k', value: 3 },
|
||||||
|
{ id: 'result', name: 'result', value: 9 },
|
||||||
|
],
|
||||||
|
calculations: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@@ -9,17 +9,17 @@
|
|||||||
|
|
||||||
/** Phases of algorithm explanation */
|
/** Phases of algorithm explanation */
|
||||||
export type VisualizationPhase =
|
export type VisualizationPhase =
|
||||||
| "problem"
|
| 'problem'
|
||||||
| "intuition"
|
| 'intuition'
|
||||||
| "pattern"
|
| 'pattern'
|
||||||
| "code"
|
| 'code'
|
||||||
| "execution";
|
| 'execution';
|
||||||
|
|
||||||
/** State of an individual array element */
|
/** State of an individual array element */
|
||||||
export interface ArrayElementState {
|
export interface ArrayElementState {
|
||||||
value: number;
|
value: number;
|
||||||
index: number;
|
index: number;
|
||||||
state: "normal" | "highlighted" | "dimmed" | "success" | "comparing";
|
state: 'normal' | 'highlighted' | 'dimmed' | 'success' | 'comparing';
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Complete array state */
|
/** Complete array state */
|
||||||
@@ -34,7 +34,7 @@ export interface PointerState {
|
|||||||
id: string;
|
id: string;
|
||||||
name: string;
|
name: string;
|
||||||
index: number;
|
index: number;
|
||||||
color: "left" | "right" | "mid" | "default";
|
color: 'left' | 'right' | 'mid' | 'default';
|
||||||
showValue?: boolean;
|
showValue?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,13 +59,13 @@ export interface CalculationState {
|
|||||||
id: string;
|
id: string;
|
||||||
expression: string;
|
expression: string;
|
||||||
result: string;
|
result: string;
|
||||||
position: "above" | "below" | "inline";
|
position: 'above' | 'below' | 'inline';
|
||||||
anchorElementId?: string;
|
anchorElementId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Animation descriptor for transitions */
|
/** Animation descriptor for transitions */
|
||||||
export interface Animation {
|
export interface Animation {
|
||||||
type: "move" | "highlight" | "fade" | "appear" | "disappear" | "calculate";
|
type: 'move' | 'highlight' | 'fade' | 'appear' | 'disappear' | 'calculate';
|
||||||
targetId: string;
|
targetId: string;
|
||||||
duration?: number;
|
duration?: number;
|
||||||
delay?: number;
|
delay?: number;
|
||||||
@@ -93,7 +93,7 @@ export interface VisualizationStep {
|
|||||||
|
|
||||||
/** Code definition for the algorithm */
|
/** Code definition for the algorithm */
|
||||||
export interface AlgorithmCode {
|
export interface AlgorithmCode {
|
||||||
language: "typescript" | "python" | "javascript";
|
language: 'typescript' | 'python' | 'javascript';
|
||||||
code: string;
|
code: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
"use client";
|
'use client';
|
||||||
|
|
||||||
import { useCallback, useEffect, useRef, useState } from "react";
|
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import type {
|
import type {
|
||||||
AlgorithmDefinition,
|
AlgorithmDefinition,
|
||||||
PlaybackSpeed,
|
PlaybackSpeed,
|
||||||
PlaybackState,
|
PlaybackState,
|
||||||
UseVisualizationReturn,
|
UseVisualizationReturn,
|
||||||
VisualizationControls,
|
VisualizationControls,
|
||||||
} from "./types";
|
} from './types';
|
||||||
|
|
||||||
const SPEED_MULTIPLIERS: Record<PlaybackSpeed, number> = {
|
const SPEED_MULTIPLIERS: Record<PlaybackSpeed, number> = {
|
||||||
0.5: 2,
|
0.5: 2,
|
||||||
@@ -142,31 +142,31 @@ export function useVisualization(
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case " ":
|
case ' ':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
togglePlay();
|
togglePlay();
|
||||||
break;
|
break;
|
||||||
case "ArrowLeft":
|
case 'ArrowLeft':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
stepBackward();
|
stepBackward();
|
||||||
break;
|
break;
|
||||||
case "ArrowRight":
|
case 'ArrowRight':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
stepForward();
|
stepForward();
|
||||||
break;
|
break;
|
||||||
case "Home":
|
case 'Home':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
goToFirst();
|
goToFirst();
|
||||||
break;
|
break;
|
||||||
case "End":
|
case 'End':
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
goToLast();
|
goToLast();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
window.addEventListener("keydown", handleKeyDown);
|
window.addEventListener('keydown', handleKeyDown);
|
||||||
return () => window.removeEventListener("keydown", handleKeyDown);
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
}, [togglePlay, stepBackward, stepForward, goToFirst, goToLast]);
|
}, [togglePlay, stepBackward, stepForward, goToFirst, goToLast]);
|
||||||
|
|
||||||
const currentStep = algorithm.steps[playback.currentStepIndex];
|
const currentStep = algorithm.steps[playback.currentStepIndex];
|
||||||
|
|||||||
Reference in New Issue
Block a user