Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ A free, interactive web tool to learn algorithms through animated step-by-step v
- **Variable tracking** — see the state of every variable in real time
- **Contextual explanation** — understand the _why_ behind each operation

## 40+ algorithms across 8 categories
## 41+ algorithms across 8 categories

<table>
<tr>
Expand Down Expand Up @@ -86,7 +86,7 @@ Tower of Hanoi

### Concepts

Big O · Recursion · Two Pointers · Sliding Window · Memoization · Greedy vs DP · Space Complexity
Big O · Recursion · Two Pointers · Sliding Window · Prefix Sum Array · Memoization · Greedy vs DP · Space Complexity

</td>
</tr>
Expand Down
4 changes: 2 additions & 2 deletions README_ES.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Una herramienta web interactiva y gratuita para aprender algoritmos a través de
- **Seguimiento de variables** — ve el estado de cada variable en tiempo real
- **Explicación contextual** — entiende el _porqué_ de cada operación

## +40 algoritmos en 8 categorías
## +41 algoritmos en 8 categorías

<table>
<tr>
Expand Down Expand Up @@ -86,7 +86,7 @@ Torre de Hanói

### Conceptos

Big O · Recursión · Two Pointers · Sliding Window · Memoización · Greedy vs DP · Space Complexity
Big O · Recursión · Two Pointers · Sliding Window · Prefix Sum Array · Memoización · Greedy vs DP · Space Complexity

</td>
</tr>
Expand Down
46 changes: 46 additions & 0 deletions src/content/algorithms/prefix-sum-array.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type { Locale } from '@i18n/translations'

const descriptions: Record<Locale, string> = {
en: `Prefix Sum Array

A Prefix Sum Array preprocesses a static array so range-sum queries become O(1). Each position stores the sum of all elements up to that index.

How it works:
1. Build prefix[0] = arr[0]
2. For each next index, add the current value to the previous prefix
3. Answer sum(l, r) with prefix[r] - prefix[l - 1]
4. If l = 0, the answer is just prefix[r]

Time Complexity: O(n) preprocessing, O(1) per query
Space Complexity: O(n)

Best when:
- The array is static
- You need many range-sum queries
- You want to trade one preprocessing pass for instant lookups

Limitation:
- Point updates are not handled efficiently here; for dynamic updates use other structures such as Fenwick Tree or Segment Tree.`,
es: `Prefix Sum Array

Un Prefix Sum Array preprocesa un arreglo estático para que las consultas de suma por rango sean O(1). Cada posición guarda la suma de todos los elementos hasta ese índice.

Cómo funciona:
1. Construir prefix[0] = arr[0]
2. Para cada índice siguiente, sumar el valor actual al prefijo anterior
3. Responder sum(l, r) con prefix[r] - prefix[l - 1]
4. Si l = 0, la respuesta es simplemente prefix[r]

Complejidad Temporal: O(n) de preprocesamiento, O(1) por consulta
Complejidad Espacial: O(n)

Conviene cuando:
- El arreglo es estático
- Necesitas muchas consultas de suma por rango
- Quieres cambiar una pasada de preprocesamiento por consultas instantáneas

Limitación:
- Las actualizaciones puntuales no se manejan eficientemente aquí; para actualizaciones dinámicas hacen falta otras estructuras como Fenwick Tree o Segment Tree.`,
}

export default descriptions
7 changes: 7 additions & 0 deletions src/lib/algorithms/catalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export const algorithmCatalog: AlgorithmSummary[] = [
difficulty: 'intermediate',
visualization: 'concept',
},
{
id: 'prefix-sum-array',
name: 'Prefix Sum Array',
category: 'Concepts',
difficulty: 'easy',
visualization: 'concept',
},
{
id: 'space-complexity',
name: 'Space Complexity',
Expand Down
172 changes: 172 additions & 0 deletions src/lib/algorithms/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1205,6 +1205,178 @@ export const slidingWindow: Algorithm = {
},
}

// ============================================================
// PREFIX SUM ARRAY
// ============================================================

export const prefixSumArray: Algorithm = {
id: 'prefix-sum-array',
name: 'Prefix Sum Array',
category: 'Concepts',
difficulty: 'easy',
visualization: 'concept',
code: `function buildPrefixSum(arr) {
const prefix = new Array(arr.length);
prefix[0] = arr[0];

for (let i = 1; i < arr.length; i++) {
prefix[i] = prefix[i - 1] + arr[i];
}
return prefix;
}

function rangeSum(prefix, left, right) {
if (left === 0) {
return prefix[right];
}
return prefix[right] - prefix[left - 1];
}`,

generateSteps(locale = 'en') {
const steps: Step[] = []
const array = [3, 1, 4, 2, 5]
const fullPrefix = [3, 4, 8, 10, 15]

const partialPrefix = (filledThrough: number) =>
array.map((_, index) => (index <= filledThrough ? fullPrefix[index] : null))

steps.push({
concept: {
type: 'prefixSum',
array,
prefix: array.map(() => null),
phase: 'intro',
currentIndex: null,
range: null,
activePrefixIndices: [],
query: null,
operation: 'preprocess static array',
},
description: d(
locale,
'Prefix sums trade one O(n) preprocessing pass for O(1) range-sum queries on a static array.',
'Los prefix sums cambian una pasada de preprocesamiento O(n) por consultas de suma por rango O(1) sobre un arreglo estático.',
),
codeLine: 1,
variables: { array: '[3, 1, 4, 2, 5]', preprocessing: 'O(n)', 'per query': 'O(1)' },
})

steps.push({
concept: {
type: 'prefixSum',
array,
prefix: partialPrefix(0),
phase: 'build',
currentIndex: 0,
range: { start: 0, end: 0 },
activePrefixIndices: [0],
query: null,
operation: 'build prefix[0]',
},
description: d(
locale,
'Initialize the prefix array with the first value. prefix[0] equals arr[0], so the sum from 0 to 0 is already known.',
'Inicializa el arreglo prefix con el primer valor. prefix[0] es igual a arr[0], así que la suma de 0 a 0 ya queda conocida.',
),
codeLine: 3,
variables: { i: 0, 'arr[i]': 3, 'prefix[0]': 3 },
})

for (let i = 1; i < array.length; i++) {
steps.push({
concept: {
type: 'prefixSum',
array,
prefix: partialPrefix(i),
phase: 'build',
currentIndex: i,
range: { start: 0, end: i },
activePrefixIndices: [i - 1, i],
query: null,
operation: `build prefix[${i}]`,
},
description: d(
locale,
`Add arr[${i}] = ${array[i]} to the previous prefix. Now prefix[${i}] stores the sum of the whole range [0..${i}] = ${fullPrefix[i]}.`,
`Suma arr[${i}] = ${array[i]} al prefijo anterior. Ahora prefix[${i}] guarda la suma de todo el rango [0..${i}] = ${fullPrefix[i]}.`,
),
codeLine: i === 1 ? 5 : 6,
variables: {
i,
'prefix[i - 1]': fullPrefix[i - 1],
'arr[i]': array[i],
'prefix[i]': fullPrefix[i],
},
})
}

steps.push({
concept: {
type: 'prefixSum',
array,
prefix: partialPrefix(array.length - 1),
phase: 'query',
currentIndex: null,
range: { start: 0, end: 2 },
activePrefixIndices: [2],
query: { left: 0, right: 2, sum: 8, usesBaseCase: true },
operation: 'query sum(0, 2)',
},
description: d(
locale,
'Base case: if the range starts at index 0, the answer is just prefix[right]. sum(0, 2) = prefix[2] = 8.',
'Caso base: si el rango empieza en el índice 0, la respuesta es simplemente prefix[right]. sum(0, 2) = prefix[2] = 8.',
),
codeLine: 12,
variables: { left: 0, right: 2, answer: 8, formula: 'prefix[2]' },
})

steps.push({
concept: {
type: 'prefixSum',
array,
prefix: partialPrefix(array.length - 1),
phase: 'query',
currentIndex: null,
range: { start: 1, end: 4 },
activePrefixIndices: [0, 4],
query: { left: 1, right: 4, sum: 12, usesBaseCase: false },
operation: 'query sum(1, 4)',
},
description: d(
locale,
'General case: subtract the prefix before the range. sum(1, 4) = prefix[4] - prefix[0] = 15 - 3 = 12.',
'Caso general: resta el prefijo anterior al rango. sum(1, 4) = prefix[4] - prefix[0] = 15 - 3 = 12.',
),
codeLine: 15,
variables: { left: 1, right: 4, 'prefix[4]': 15, 'prefix[0]': 3, answer: 12 },
})

steps.push({
concept: {
type: 'prefixSum',
array,
prefix: partialPrefix(array.length - 1),
phase: 'done',
currentIndex: null,
range: { start: 1, end: 4 },
activePrefixIndices: [0, 2, 4],
query: { left: 1, right: 4, sum: 12, usesBaseCase: false },
operation: 'ready for many queries',
},
description: d(
locale,
'After one linear build, every later query is O(1). That is why prefix sums shine when the array is static and queries are frequent.',
'Después de una construcción lineal, cada consulta posterior es O(1). Por eso los prefix sums destacan cuando el arreglo es estático y las consultas son frecuentes.',
),
codeLine: 11,
variables: { preprocessing: 'O(n)', 'per query': 'O(1)', limitation: 'no dynamic updates' },
})

return steps
},
}

// ============================================================
// SPACE COMPLEXITY (reuses BigO chart)
// ============================================================
Expand Down
17 changes: 17 additions & 0 deletions src/lib/algorithms/cpp/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,23 @@ vector<int> mergeSort(vector<int> arr) {
return s.substr(bestStart, best);
}`),

'prefix-sum-array': annotated(`vector<int> buildPrefixSum(const vector<int>& arr) { //@1
vector<int> prefix(arr.size());
prefix[0] = arr[0]; //@3

for (int i = 1; i < (int)arr.size(); i++) { //@5
prefix[i] = prefix[i - 1] + arr[i]; //@6
}
return prefix; //@8
}

int rangeSum(const vector<int>& prefix, int left, int right) { //@11
if (left == 0) { //@12
return prefix[right]; //@13
}
return prefix[right] - prefix[left - 1]; //@15
}`),

'space-complexity': annotated(`// O(1) space — fixed variables //@1
void swap(vector<int>& arr, int i, int j) { //@2
int temp = arr[i];
Expand Down
2 changes: 2 additions & 0 deletions src/lib/algorithms/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
recursion,
twoPointers,
slidingWindow,
prefixSumArray,
spaceComplexity,
memoization,
greedyVsDp,
Expand Down Expand Up @@ -71,6 +72,7 @@ export const algorithms: Algorithm[] = [
recursion,
twoPointers,
slidingWindow,
prefixSumArray,
spaceComplexity,
memoization,
greedyVsDp,
Expand Down
17 changes: 17 additions & 0 deletions src/lib/algorithms/java/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,23 @@ int[] mergeSort(int[] arr) {
return s.substring(bestStart, bestStart + best);
}`),

'prefix-sum-array': annotated(`int[] buildPrefixSum(int[] arr) { //@1
int[] prefix = new int[arr.length];
prefix[0] = arr[0]; //@3

for (int i = 1; i < arr.length; i++) { //@5
prefix[i] = prefix[i - 1] + arr[i]; //@6
}
return prefix; //@8
}

int rangeSum(int[] prefix, int left, int right) { //@11
if (left == 0) { //@12
return prefix[right]; //@13
}
return prefix[right] - prefix[left - 1]; //@15
}`),

'space-complexity': annotated(`// O(1) space — fixed variables //@1
void swap(int[] arr, int i, int j) { //@2
int temp = arr[i];
Expand Down
2 changes: 2 additions & 0 deletions src/lib/algorithms/loaders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ const ALGORITHM_LOADERS: Record<string, () => Promise<Algorithm>> = {
recursion: () => import('./concepts?algorithm=recursion').then(readDefaultAlgorithm),
'two-pointers': () => import('./concepts?algorithm=twoPointers').then(readDefaultAlgorithm),
'sliding-window': () => import('./concepts?algorithm=slidingWindow').then(readDefaultAlgorithm),
'prefix-sum-array': () =>
import('./concepts?algorithm=prefixSumArray').then(readDefaultAlgorithm),
'space-complexity': () =>
import('./concepts?algorithm=spaceComplexity').then(readDefaultAlgorithm),
memoization: () => import('./concepts?algorithm=memoization').then(readDefaultAlgorithm),
Expand Down
13 changes: 13 additions & 0 deletions src/lib/algorithms/python/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,19 @@ def merge(left, right):
best_start = start
return s[best_start:best_start + best]`),

'prefix-sum-array': annotated(`def build_prefix_sum(arr): #@1
prefix = [0] * len(arr)
prefix[0] = arr[0] #@3

for i in range(1, len(arr)): #@5
prefix[i] = prefix[i - 1] + arr[i] #@6
return prefix #@8

def range_sum(prefix, left, right): #@11
if left == 0: #@12
return prefix[right] #@13
return prefix[right] - prefix[left - 1] #@15`),

'space-complexity': annotated(`# O(1) space — fixed variables #@1
def swap(arr, i, j): #@2
temp = arr[i]
Expand Down
17 changes: 17 additions & 0 deletions src/lib/algorithms/rust/concepts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,23 @@ fn merge_sort(arr: &[i32]) -> Vec<i32> {
chars[best_start..best_start + best].iter().collect()
}`),

'prefix-sum-array': annotated(`fn build_prefix_sum(arr: &[i32]) -> Vec<i32> { //@1
let mut prefix = vec![0; arr.len()];
prefix[0] = arr[0]; //@3

for i in 1..arr.len() { //@5
prefix[i] = prefix[i - 1] + arr[i]; //@6
}
prefix //@8
}

fn range_sum(prefix: &[i32], left: usize, right: usize) -> i32 { //@11
if left == 0 { //@12
return prefix[right]; //@13
}
prefix[right] - prefix[left - 1] //@15
}`),

'space-complexity': annotated(`// O(1) space — fixed variables //@1
fn swap(arr: &mut [i32], i: usize, j: usize) { //@2
let temp = arr[i];
Expand Down
Loading