feat(viz): two pointers narrative

This commit is contained in:
2025-08-24 15:30:46 +01:00
parent 1031af4e3b
commit 2e4de5cc31
17 changed files with 2191 additions and 2 deletions
+802
View File
@@ -0,0 +1,802 @@
import type { AlgorithmDefinition } from "@/lib/visualizations/types";
export const twoSumAlgorithm: AlgorithmDefinition = {
id: "two-sum-sorted",
title: "Two Sum II - Sorted Array",
slug: "two-sum-sorted",
pattern: {
name: "Two Pointers",
description:
"Use two pointers moving toward each other to find a pair that satisfies a condition.",
},
problemStatement:
"Given a sorted array of integers, find two numbers that add up to a target value. Return their indices.",
intuition:
"Because the array is sorted, we can use two pointers starting at opposite ends. If the sum is too small, we move the left pointer right. If too large, we move the right pointer left.",
code: {
language: "python",
code: `def two_sum(nums: list[int], target: int) -> list[int]:
left = 0
right = len(nums) - 1
while left < right:
total = nums[left] + nums[right]
if total == target:
return [left, right]
elif total < target:
left += 1
else:
right -= 1
return [] # No solution found`,
},
initialExample: {
input: { nums: [2, 7, 11, 15], target: 9 },
expected: [0, 1],
},
steps: [
// Phase 1: Problem (2 steps)
{
id: "problem-1",
phase: "problem",
explanation:
"We have a sorted array [2, 7, 11, 15] and need to find two numbers that add up to 9.",
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "problem-2",
phase: "problem",
explanation:
"A brute force approach would check every pair, giving O(n²) time complexity. Can we do better using the sorted property?",
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "highlighted" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
// Phase 2: Intuition (3 steps)
{
id: "intuition-1",
phase: "intuition",
explanation:
"Key insight: In a sorted array, the smallest values are on the left and largest on the right.",
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "highlighted" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "intuition-2",
phase: "intuition",
explanation:
"If we start with pointers at both ends, we can adjust based on whether our sum is too small or too large.",
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "dimmed" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "highlighted" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left" },
{ id: "right", name: "right", index: 3, color: "right" },
],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "intuition-3",
phase: "intuition",
explanation:
"Sum too small? Move left pointer right to increase the sum. Sum too large? Move right pointer left to decrease it.",
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "highlighted" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left" },
{ id: "right", name: "right", index: 3, color: "right" },
],
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 the start (index 0), right at the end (index n-1).",
codeLine: 2,
codeHighlightLines: [2, 3],
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "code-2",
phase: "code",
explanation: "Continue while the pointers have not crossed each other.",
codeLine: 5,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "code-3",
phase: "code",
explanation:
"Calculate the total of elements at both pointers and compare to target.",
codeLine: 6,
codeHighlightLines: [6, 8],
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "code-4",
phase: "code",
explanation: "If total is too small, increment left to try a larger value.",
codeLine: 10,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [],
variables: [{ id: "target", name: "target", value: 9 }],
calculations: [],
},
},
{
id: "code-5",
phase: "code",
explanation:
"If total is too large, decrement right to try a smaller value.",
codeLine: 12,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "normal" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, 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 pointing to value 2.",
codeLine: 2,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "normal" },
],
},
],
pointers: [{ id: "left", name: "left", index: 0, color: "left", showValue: true }],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
],
calculations: [],
},
},
{
id: "exec-2",
phase: "execution",
explanation: "Initialize right = 3 pointing to value 15.",
codeLine: 3,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "highlighted" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 3, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 3 },
],
calculations: [],
},
},
{
id: "exec-3",
phase: "execution",
explanation: "Check loop condition: left (0) < right (3)? Yes, enter loop.",
codeLine: 5,
decision: {
question: "Is left < right?",
answer: "0 < 3 = true",
action: "Enter the while loop",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "highlighted" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 3, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 3 },
],
calculations: [],
},
},
{
id: "exec-4",
phase: "execution",
explanation: "Calculate total = nums[0] + nums[3] = 2 + 15 = 17.",
codeLine: 6,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "comparing" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "comparing" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 3, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 3 },
{ id: "total", name: "total", value: 17, derivation: "2 + 15" },
],
calculations: [
{ id: "calc-1", expression: "2 + 15", result: "17", position: "above" },
],
},
},
{
id: "exec-5",
phase: "execution",
explanation: "Compare: total (17) vs target (9). Total is too large!",
codeLine: 8,
decision: {
question: "Is total == target?",
answer: "17 ≠ 9",
action: "Check if total < target",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "comparing" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "comparing" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 3, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 3 },
{ id: "total", name: "total", value: 17 },
],
calculations: [
{ id: "calc-1", expression: "17 > 9", result: "true", position: "above" },
],
},
},
{
id: "exec-6",
phase: "execution",
explanation:
"Total (17) > target (9), so we need a smaller value. Decrement right pointer.",
codeLine: 12,
decision: {
question: "Is total > target?",
answer: "17 > 9 = true",
action: "Move right pointer left (right--)",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "normal" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 3, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 3 },
{ id: "total", name: "total", value: 17 },
],
calculations: [],
},
},
{
id: "exec-7",
phase: "execution",
explanation: "Right pointer moves from index 3 to index 2 (value 11).",
codeLine: 12,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "highlighted" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 2, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 2, previousValue: 3 },
{ id: "total", name: "total", value: 17 },
],
calculations: [],
},
},
{
id: "exec-8",
phase: "execution",
explanation: "Check loop condition: left (0) < right (2)? Yes, continue.",
codeLine: 5,
decision: {
question: "Is left < right?",
answer: "0 < 2 = true",
action: "Continue the while loop",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "highlighted" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 2, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 2 },
],
calculations: [],
},
},
{
id: "exec-9",
phase: "execution",
explanation: "Calculate total = nums[0] + nums[2] = 2 + 11 = 13.",
codeLine: 6,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "comparing" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "comparing" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 2, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 2 },
{ id: "total", name: "total", value: 13, previousValue: 17, derivation: "2 + 11" },
],
calculations: [
{ id: "calc-2", expression: "2 + 11", result: "13", position: "above" },
],
},
},
{
id: "exec-10",
phase: "execution",
explanation: "Compare: total (13) vs target (9). Still too large!",
codeLine: 8,
decision: {
question: "Is total == target?",
answer: "13 ≠ 9",
action: "Check if total > target",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "comparing" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "comparing" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 2, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 2 },
{ id: "total", name: "total", value: 13 },
],
calculations: [
{ id: "calc-2", expression: "13 > 9", result: "true", position: "above" },
],
},
},
{
id: "exec-11",
phase: "execution",
explanation:
"Total (13) > target (9). Decrement right pointer again.",
codeLine: 12,
decision: {
question: "Is total > target?",
answer: "13 > 9 = true",
action: "Move right pointer left (right--)",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "normal" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 2, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 2 },
{ id: "total", name: "total", value: 13 },
],
calculations: [],
},
},
{
id: "exec-12",
phase: "execution",
explanation: "Right pointer moves from index 2 to index 1 (value 7).",
codeLine: 12,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "highlighted" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 1, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 1, previousValue: 2 },
{ id: "total", name: "total", value: 13 },
],
calculations: [],
},
},
{
id: "exec-13",
phase: "execution",
explanation: "Check loop condition: left (0) < right (1)? Yes, continue.",
codeLine: 5,
decision: {
question: "Is left < right?",
answer: "0 < 1 = true",
action: "Continue the while loop",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "highlighted" },
{ value: 7, index: 1, state: "highlighted" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 1, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 1 },
],
calculations: [],
},
},
{
id: "exec-14",
phase: "execution",
explanation: "Calculate total = nums[0] + nums[1] = 2 + 7 = 9.",
codeLine: 6,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "comparing" },
{ value: 7, index: 1, state: "comparing" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 1, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 1 },
{ id: "total", name: "total", value: 9, previousValue: 13, derivation: "2 + 7" },
],
calculations: [
{ id: "calc-3", expression: "2 + 7", result: "9", position: "above" },
],
},
},
{
id: "exec-15",
phase: "execution",
explanation: "Compare: total (9) == target (9). Found it!",
codeLine: 8,
decision: {
question: "Is total == target?",
answer: "9 == 9 = true",
action: "Return the indices [0, 1]",
},
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "success" },
{ value: 7, index: 1, state: "success" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 1, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "left", name: "left", value: 0 },
{ id: "right", name: "right", value: 1 },
{ id: "total", name: "total", value: 9 },
],
calculations: [
{ id: "calc-3", expression: "9 == 9", result: "true", position: "above" },
],
},
},
{
id: "exec-16",
phase: "execution",
explanation:
"Return [0, 1]. We found the answer in O(n) time using only O(1) extra space!",
codeLine: 9,
dataState: {
arrays: [
{
id: "nums",
elements: [
{ value: 2, index: 0, state: "success" },
{ value: 7, index: 1, state: "success" },
{ value: 11, index: 2, state: "dimmed" },
{ value: 15, index: 3, state: "dimmed" },
],
},
],
pointers: [
{ id: "left", name: "left", index: 0, color: "left", showValue: true },
{ id: "right", name: "right", index: 1, color: "right", showValue: true },
],
variables: [
{ id: "target", name: "target", value: 9 },
{ id: "result", name: "result", value: "[0, 1]" },
],
calculations: [],
},
},
],
};