feat(viz): sprint 1 - array visualisations

This commit is contained in:
2025-08-24 16:09:24 +01:00
parent 3caa628d59
commit 18381cea6b
13 changed files with 2702 additions and 187 deletions

View 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' },
],
},
},
],
};