Skip to content

Commit d0f7f45

Browse files
author
Damiano Bacci
committed
feat: basic app layout using mui, created dockerfile to run locally, added function to search exercises chapters
1 parent 72a1d7e commit d0f7f45

20 files changed

Lines changed: 1172 additions & 71 deletions

File tree

.dockerignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
ui/node_modules
2+
ui/.next
3+
ui/.turbo
4+
**/.DS_Store

.github/workflows/cd.yml

Whitespace-only changes.

Dockerfile

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
FROM node:22-alpine AS builder
2+
WORKDIR /project
3+
4+
COPY . .
5+
6+
WORKDIR /project/ui
7+
RUN npm ci
8+
RUN npm run build
9+
10+
FROM node:22-alpine AS runner
11+
ENV NODE_ENV=production
12+
WORKDIR /app
13+
14+
COPY --from=builder /project/ui/.next/standalone ./
15+
COPY --from=builder /project/ui/.next/static ./.next/static
16+
COPY --from=builder /project/ui/public ./public
17+
18+
EXPOSE 3000
19+
CMD ["node", "server.js"]

readme.MD

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
# A Common Sense Guide to Data Structures and Algorithms by Jay Wengrow
22

33
Exercises, examples, plus a webapp to learn and play.
4+
5+
## Run the webapp using Docker
6+
7+
First build the image with `docker build -t dsa-app .` and then run `docker run -p 3000:3000 dsa-app`.
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import Typography from '@mui/material/Typography';
2+
import Box from '@mui/material/Box';
3+
import Chip from '@mui/material/Chip';
4+
import Divider from '@mui/material/Divider';
5+
import { StorageIcon } from '@/app/components/icons';
6+
import CodeBlock from '@/app/components/CodeBlock';
7+
import { getExercise } from '@/utils/exercises';
8+
9+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
10+
return (
11+
<Box sx={{ mb: 4 }}>
12+
<Typography variant="h6" fontWeight="bold" sx={{ mb: 1 }}>
13+
{title}
14+
</Typography>
15+
{children}
16+
</Box>
17+
);
18+
}
19+
20+
export default function WhyDataStructuresMatter() {
21+
const printNumber = getExercise(1, '1_print_number.py');
22+
23+
return (
24+
<Box>
25+
<Chip label="Chapter 1" color="primary" size="small" sx={{ mb: 2 }} />
26+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, mb: 1 }}>
27+
<StorageIcon color="primary" />
28+
<Typography variant="h4" fontWeight="bold">
29+
Why Data Structures Matter
30+
</Typography>
31+
</Box>
32+
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
33+
La struttura dati che scegli determina quali operazioni puoi eseguire sui tuoi dati
34+
e quanto velocemente puoi eseguirle.
35+
</Typography>
36+
37+
<Divider sx={{ mb: 4 }} />
38+
39+
<Section title="Cos'è una struttura dati?">
40+
<Typography variant="body1" sx={{ mb: 2 }}>
41+
Una <strong>struttura dati</strong> è un modo di organizzare i dati in memoria.
42+
Le strutture dati più semplici sono gli <strong>array</strong>, ma ne esistono molte altre:
43+
set, hash table, stack, queue, linked list, alberi e grafi.
44+
</Typography>
45+
<Typography variant="body1" sx={{ mb: 2 }}>
46+
La scelta della struttura giusta può far passare un&apos;operazione da <em>lenta</em> a <em>istantanea</em>.
47+
</Typography>
48+
</Section>
49+
50+
<Section title="L'array: la struttura base">
51+
<Typography variant="body1" sx={{ mb: 2 }}>
52+
Un array è una lista ordinata di elementi. Le operazioni fondamentali su un array sono:
53+
</Typography>
54+
<Box component="ul" sx={{ pl: 2 }}>
55+
{[
56+
['Read', 'leggere un elemento in una posizione specifica'],
57+
['Search', 'trovare il valore di un elemento senza conoscerne la posizione'],
58+
['Insert', 'aggiungere un elemento in una posizione specifica'],
59+
['Delete', 'rimuovere un elemento'],
60+
].map(([op, desc]) => (
61+
<li key={op}>
62+
<Typography variant="body1">
63+
<strong>{op}</strong>: {desc}
64+
</Typography>
65+
</li>
66+
))}
67+
</Box>
68+
</Section>
69+
70+
<Section title="Read: O(1)">
71+
<Typography variant="body1" sx={{ mb: 2 }}>
72+
Leggere un elemento da un array è <strong>istantaneo</strong>: il computer conosce
73+
l&apos;indirizzo in memoria del primo elemento e, dato l&apos;indice, calcola direttamente
74+
l&apos;indirizzo dell&apos;elemento cercato.
75+
</Typography>
76+
<CodeBlock>{'array[3] // 1 solo step, sempre'}</CodeBlock>
77+
</Section>
78+
79+
<Section title="Search: O(N)">
80+
<Typography variant="body1" sx={{ mb: 2 }}>
81+
Cercare un valore senza conoscerne l&apos;indice richiede di scorrere l&apos;array elemento
82+
per elemento. Nel caso peggiore (valore non presente o in ultima posizione), eseguiamo
83+
tanti step quanti sono gli elementi: <strong>N step</strong>.
84+
</Typography>
85+
<CodeBlock>{`// Linear search — worst case: N steps
86+
for (let i = 0; i < array.length; i++) {
87+
if (array[i] === target) return i;
88+
}`}</CodeBlock>
89+
</Section>
90+
91+
<Section title="Insert e Delete">
92+
<Typography variant="body1" sx={{ mb: 2 }}>
93+
Inserire o eliminare un elemento <em>in mezzo</em> a un array è costoso: tutti gli
94+
elementi successivi devono essere shiftati. L&apos;inserimento in fondo è invece O(1).
95+
</Typography>
96+
<Typography variant="body1" sx={{ mb: 2 }}>
97+
Questo è il motivo per cui in certi scenari conviene usare strutture diverse,
98+
come le <strong>linked list</strong>, che vedremo nei capitoli successivi.
99+
</Typography>
100+
</Section>
101+
102+
<Section title="Set: una variante degli array">
103+
<Typography variant="body1" sx={{ mb: 2 }}>
104+
Un <strong>set</strong> è come un array, ma non permette valori duplicati.
105+
Il costo di questa garanzia è che ogni inserimento richiede prima una ricerca
106+
(per verificare che il valore non esista già), portando la complessità a <strong>O(N)</strong>.
107+
</Typography>
108+
<Typography variant="body1" sx={{ mb: 2 }}>
109+
Quando i duplicati non hanno senso nel tuo dominio, i set sono la scelta giusta.
110+
</Typography>
111+
</Section>
112+
113+
<Section title={`Esercizio — ${printNumber.filename}`}>
114+
<Typography variant="body1" sx={{ mb: 2 }}>
115+
Due implementazioni per stampare i numeri pari fino a 100. La seconda dimezza
116+
il numero di step saltando direttamente di 2 in 2.
117+
</Typography>
118+
<CodeBlock language={printNumber.language}>{printNumber.code}</CodeBlock>
119+
</Section>
120+
</Box>
121+
);
122+
}
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import Typography from '@mui/material/Typography';
2+
import Box from '@mui/material/Box';
3+
import Chip from '@mui/material/Chip';
4+
import Divider from '@mui/material/Divider';
5+
import { PsychologyIcon } from '@/app/components/icons';
6+
import CodeBlock from '@/app/components/CodeBlock';
7+
8+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
9+
return (
10+
<Box sx={{ mb: 4 }}>
11+
<Typography variant="h6" fontWeight="bold" sx={{ mb: 1 }}>
12+
{title}
13+
</Typography>
14+
{children}
15+
</Box>
16+
);
17+
}
18+
19+
export default function WhyAlgorithmsMatter() {
20+
return (
21+
<Box>
22+
<Chip label="Chapter 2" color="primary" size="small" sx={{ mb: 2 }} />
23+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, mb: 1 }}>
24+
<PsychologyIcon color="primary" />
25+
<Typography variant="h4" fontWeight="bold">
26+
Why Algorithms Matter
27+
</Typography>
28+
</Box>
29+
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
30+
La stessa struttura dati, elaborata con algoritmi diversi, può produrre performance
31+
radicalmente differenti.
32+
</Typography>
33+
34+
<Divider sx={{ mb: 4 }} />
35+
36+
<Section title="Stessa struttura, algoritmo diverso">
37+
<Typography variant="body1" sx={{ mb: 2 }}>
38+
Nel capitolo precedente abbiamo visto che la ricerca lineare su un array richiede
39+
al massimo <strong>N step</strong>. Ma se l&apos;array è <em>ordinato</em>, possiamo
40+
fare di meglio con la <strong>Binary Search</strong>.
41+
</Typography>
42+
</Section>
43+
44+
<Section title="Binary Search: O(log N)">
45+
<Typography variant="body1" sx={{ mb: 2 }}>
46+
La Binary Search sfrutta l&apos;ordinamento per dimezzare lo spazio di ricerca
47+
a ogni step. Si parte dal valore centrale: se è quello cercato, abbiamo finito.
48+
Altrimenti si scarta metà dell&apos;array e si ripete.
49+
</Typography>
50+
<CodeBlock>{`function binarySearch(arr, target) {
51+
let low = 0;
52+
let high = arr.length - 1;
53+
54+
while (low <= high) {
55+
const mid = Math.floor((low + high) / 2);
56+
if (arr[mid] === target) return mid;
57+
if (arr[mid] < target) low = mid + 1;
58+
else high = mid - 1;
59+
}
60+
return -1;
61+
}`}</CodeBlock>
62+
<Typography variant="body1" sx={{ mb: 2 }}>
63+
Su un array di 100 elementi: la ricerca lineare può richiedere fino a 100 step,
64+
la Binary Search al massimo <strong>7</strong> (log₂ 100 ≈ 6.6).
65+
</Typography>
66+
</Section>
67+
68+
<Section title="Il confronto in numeri">
69+
<Box component="ul" sx={{ pl: 2 }}>
70+
{[
71+
['100 elementi', 'Linear: 100 step', 'Binary: 7 step'],
72+
['10.000 elementi', 'Linear: 10.000 step', 'Binary: 13 step'],
73+
['1.000.000 elementi', 'Linear: 1.000.000 step', 'Binary: 20 step'],
74+
].map(([size, lin, bin]) => (
75+
<li key={size}>
76+
<Typography variant="body1">
77+
<strong>{size}</strong>{lin} | {bin}
78+
</Typography>
79+
</li>
80+
))}
81+
</Box>
82+
</Section>
83+
84+
<Section title="Quando si può usare la Binary Search?">
85+
<Typography variant="body1" sx={{ mb: 2 }}>
86+
Solo su array <strong>ordinati</strong>. Se l&apos;array non è ordinato,
87+
bisogna prima ordinarlo — ma se le ricerche sono frequenti, il costo
88+
di ordinamento viene ammortizzato rapidamente.
89+
</Typography>
90+
<Typography variant="body1">
91+
Questo dimostra che la scelta dell&apos;algoritmo è spesso inseparabile
92+
dalla scelta della struttura dati: insieme determinano le performance
93+
complessive del programma.
94+
</Typography>
95+
</Section>
96+
</Box>
97+
);
98+
}

ui/app/3-big-o-notation/page.tsx

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import Typography from '@mui/material/Typography';
2+
import Box from '@mui/material/Box';
3+
import Chip from '@mui/material/Chip';
4+
import Divider from '@mui/material/Divider';
5+
import { TimelineIcon } from '@/app/components/icons';
6+
7+
function Section({ title, children }: { title: string; children: React.ReactNode }) {
8+
return (
9+
<Box sx={{ mb: 4 }}>
10+
<Typography variant="h6" fontWeight="bold" sx={{ mb: 1 }}>
11+
{title}
12+
</Typography>
13+
{children}
14+
</Box>
15+
);
16+
}
17+
18+
function ComplexityRow({ notation, name, example }: { notation: string; name: string; example: string }) {
19+
return (
20+
<Box
21+
sx={{
22+
display: 'flex',
23+
gap: 2,
24+
alignItems: 'flex-start',
25+
py: 1.5,
26+
borderBottom: '1px solid',
27+
borderColor: 'divider',
28+
}}
29+
>
30+
<Chip label={notation} size="small" variant="outlined" sx={{ fontFamily: 'monospace', minWidth: 80 }} />
31+
<Box>
32+
<Typography variant="body2" fontWeight="bold">{name}</Typography>
33+
<Typography variant="body2" color="text.secondary">{example}</Typography>
34+
</Box>
35+
</Box>
36+
);
37+
}
38+
39+
export default function BigONotation() {
40+
return (
41+
<Box>
42+
<Chip label="Chapter 3" color="primary" size="small" sx={{ mb: 2 }} />
43+
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, mb: 1 }}>
44+
<TimelineIcon color="primary" />
45+
<Typography variant="h4" fontWeight="bold">
46+
Yes! Big O Notation
47+
</Typography>
48+
</Box>
49+
<Typography variant="body1" color="text.secondary" sx={{ mb: 4 }}>
50+
Un linguaggio universale per descrivere l&apos;efficienza di un algoritmo,
51+
indipendentemente dall&apos;hardware o dal linguaggio di programmazione.
52+
</Typography>
53+
54+
<Divider sx={{ mb: 4 }} />
55+
56+
<Section title="Perché ci serve la Big O?">
57+
<Typography variant="body1" sx={{ mb: 2 }}>
58+
Dire &quot;questo algoritmo è veloce&quot; non basta: dipende dall&apos;hardware,
59+
dalla dimensione dell&apos;input, dal carico del sistema. La Big O ci fornisce
60+
un modo formale per descrivere come cresce il numero di step al crescere di N
61+
(la dimensione dell&apos;input).
62+
</Typography>
63+
<Typography variant="body1" sx={{ mb: 2 }}>
64+
La Big O risponde alla domanda: <em>come scala questo algoritmo?</em>
65+
</Typography>
66+
</Section>
67+
68+
<Section title="Le complessità più comuni">
69+
<ComplexityRow
70+
notation="O(1)"
71+
name="Costante"
72+
example="Leggere un elemento di un array per indice. Il numero di step è sempre 1."
73+
/>
74+
<ComplexityRow
75+
notation="O(log N)"
76+
name="Logaritmica"
77+
example="Binary Search. Ogni step dimezza lo spazio di ricerca."
78+
/>
79+
<ComplexityRow
80+
notation="O(N)"
81+
name="Lineare"
82+
example="Linear Search. Nel caso peggiore si visitano tutti gli N elementi."
83+
/>
84+
<ComplexityRow
85+
notation="O(N log N)"
86+
name="Linearitmica"
87+
example="Merge Sort, Quick Sort (avg). Il più efficiente tra gli algoritmi di ordinamento comparison-based."
88+
/>
89+
<ComplexityRow
90+
notation="O(N²)"
91+
name="Quadratica"
92+
example="Bubble Sort, Selection Sort. Un ciclo annidato su N elementi."
93+
/>
94+
</Section>
95+
96+
<Section title="La regola del caso peggiore">
97+
<Typography variant="body1" sx={{ mb: 2 }}>
98+
Per convenzione, la Big O descrive il <strong>caso peggiore</strong>.
99+
La ricerca lineare è O(N) anche se spesso trovi l&apos;elemento al primo tentativo:
100+
dobbiamo progettare tenendo conto dello scenario più sfavorevole.
101+
</Typography>
102+
</Section>
103+
104+
<Section title="Si ignorano le costanti">
105+
<Typography variant="body1" sx={{ mb: 2 }}>
106+
Un algoritmo che esegue <em>3N</em> operazioni si scrive comunque O(N), non O(3N).
107+
La Big O cattura solo il <strong>tasso di crescita</strong>, non i coefficienti.
108+
Questo perché al crescere di N, le costanti diventano irrilevanti rispetto
109+
all&apos;ordine di grandezza.
110+
</Typography>
111+
<Box
112+
sx={{
113+
backgroundColor: 'primary.50',
114+
border: '1px solid',
115+
borderColor: 'primary.200',
116+
borderRadius: 1,
117+
p: 2,
118+
mt: 1,
119+
}}
120+
>
121+
<Typography variant="body2" color="primary.main" fontWeight="bold">
122+
Regola pratica
123+
</Typography>
124+
<Typography variant="body2" color="text.secondary" sx={{ mt: 0.5 }}>
125+
O(1) &lt; O(log N) &lt; O(N) &lt; O(N log N) &lt; O(N²) &lt; O(2ᴺ) &lt; O(N!)
126+
</Typography>
127+
</Box>
128+
</Section>
129+
</Box>
130+
);
131+
}

0 commit comments

Comments
 (0)