Skip to content

Commit 5b1c623

Browse files
committed
feat: add queue chapter
1 parent aa1fbb6 commit 5b1c623

5 files changed

Lines changed: 339 additions & 7 deletions

File tree

.github/workflows/docs.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,16 +23,19 @@ jobs:
2323
test -s docs/01-vector.md
2424
test -s docs/02-linked-list.md
2525
test -s docs/03-stack.md
26+
test -s docs/04-queue.md
2627
test -s examples/README.md
2728
test -s tests/README.md
2829
test -s benches/README.md
2930
test -s benches/vector_bench.rs
3031
test -s benches/linked_list_bench.rs
3132
test -s benches/stack_bench.rs
33+
test -s benches/queue_bench.rs
3234
test -s diagrams/README.md
3335
test -s diagrams/01-vector.mmd
3436
test -s diagrams/02-linked-list.mmd
3537
test -s diagrams/03-stack.mmd
38+
test -s diagrams/04-queue.mmd
3639
test -s assets/README.md
3740
3841
grep -q "Vector" docs/SUMMARY.md

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ un algoritmo sea necesario para explicar la propia estructura.
3333
| 01 | Vector | `src/vector.rs` | benchmarked |
3434
| 02 | Linked List | `src/linked_list.rs` | benchmarked |
3535
| 03 | Stack | `src/stack.rs` | benchmarked |
36-
| 04 | Queue | `src/queue.rs` | planned |
36+
| 04 | Queue | `src/queue.rs` | benchmarked |
3737
| 05 | Deque | `src/deque.rs` | planned |
3838
| 06 | Heap | `src/heap.rs` | planned |
3939
| 07 | Trie | `src/trie.rs` | planned |

ROADMAP.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ El checklist detallado vive en
2222
| 01 | Vector | benchmarked |
2323
| 02 | Linked List | benchmarked |
2424
| 03 | Stack | benchmarked |
25-
| 04 | Queue | planned |
25+
| 04 | Queue | benchmarked |
2626
| 05 | Deque | planned |
2727
| 06 | Heap | planned |
2828
| 07 | Trie | planned |

docs/04-queue.md

Lines changed: 329 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,329 @@
1+
# Queue
2+
3+
> **Curso:** rust-data-structures · **Capitulo:** 04 · **Prerequisitos:** Capitulo 01, Vector; Capitulo 02, Linked List
4+
> **Codigo:** [`src/queue.rs`](../src/queue.rs) · **Video:** pendiente
5+
> **Leccion en el sitio:** pendiente
6+
7+
## Introduccion
8+
9+
Una cola (*queue*) es una coleccion FIFO: *first in, first out*. El primer valor
10+
que entra es el primero que sale. Esa regla aparece cada vez que queremos
11+
respetar orden de llegada: solicitudes, tareas pendientes, turnos de atencion o
12+
fronteras de exploracion.
13+
14+
En este capitulo implementamos `Queue<T>` como buffer circular seguro. La API
15+
expone `enqueue`, `dequeue`, `front`, `back`, `clear` e `iter`. La representacion
16+
usa un arreglo de ranuras opcionales, un indice `head` y una longitud logica.
17+
Asi separamos el contrato FIFO de los detalles fisicos de memoria.
18+
19+
## Motivacion
20+
21+
Una cola ingenua puede construirse con un vector: insertar al final y remover del
22+
frente. El problema es que remover el primer elemento obliga a desplazar todos
23+
los elementos restantes. Si haces eso miles de veces, pagas un costo que la API
24+
oculta pero el sistema siente.
25+
26+
La cola existe para decir algo mas preciso: no necesitamos acceso arbitrario ni
27+
inserciones en medio. Necesitamos aceptar valores por un extremo y entregarlos
28+
por el otro. Esa restriccion permite usar un buffer circular y reutilizar espacio
29+
sin mover todos los elementos en cada `dequeue`.
30+
31+
## Teoria
32+
33+
### Historia
34+
35+
Las colas son una de las estructuras clasicas de sistemas. Aparecen en
36+
schedulers, buffers de red, colas de impresion, simulaciones, procesamiento por
37+
lotes y recorridos BFS. En todos esos escenarios hay una idea de justicia o de
38+
orden temporal: lo que llego antes debe tener oportunidad antes.
39+
40+
Tambien son una pieza de diseno. Elegir una cola comunica que el orden importa,
41+
pero solo como orden de llegada. Si el siguiente elemento debe elegirse por
42+
prioridad, la estructura correcta ya no es una cola simple; es un heap o una
43+
cola de prioridad.
44+
45+
### Fundamentos
46+
47+
La cola expone estas operaciones:
48+
49+
- `enqueue(value)`: agrega un valor al fondo.
50+
- `dequeue()`: remueve el frente y devuelve su valor.
51+
- `front()`: lee el frente sin removerlo.
52+
- `back()`: lee el fondo sin removerlo.
53+
- `clear()`: vacia la cola.
54+
- `iter()`: recorre del frente al fondo.
55+
56+
La invariante semantica es:
57+
58+
```text
59+
si enqueue(a) ocurre antes que enqueue(b), entonces dequeue() devuelve a antes que b
60+
```
61+
62+
La invariante de representacion de nuestro buffer circular es:
63+
64+
```text
65+
indice_fisico = (head + desplazamiento_logico) % capacity
66+
```
67+
68+
`head` apunta al frente logico. `len` dice cuantos valores validos existen. El
69+
fondo no se guarda como campo: se calcula con `head + len - 1`. Para insertar,
70+
usamos `(head + len) % capacity`.
71+
72+
### Casos de uso
73+
74+
Usos clasicos:
75+
76+
- Schedulers FIFO.
77+
- Fronteras de BFS.
78+
- Procesamiento de solicitudes.
79+
- Buffers de eventos.
80+
- Simulaciones de turnos.
81+
- Pipelines donde el productor y el consumidor trabajan a ritmos distintos.
82+
83+
### Ventajas y limitaciones
84+
85+
Ventajas:
86+
87+
- API pequena y expresiva.
88+
- `enqueue`, `dequeue`, `front` y `back` baratos.
89+
- Underflow representado con `Option<T>`, sin panico.
90+
- Reutiliza ranuras liberadas por `dequeue`.
91+
- Evita desplazar todos los elementos al remover del frente.
92+
93+
Limitaciones:
94+
95+
- No ofrece acceso arbitrario; esa restriccion protege el contrato FIFO.
96+
- `enqueue` puede crecer y mover valores cuando la capacidad se agota.
97+
- La aritmetica modular vuelve mas delicadas las invariantes internas.
98+
- No resuelve prioridades; si el orden depende de peso o urgencia, usa un heap.
99+
100+
### Comparacion con alternativas
101+
102+
Una cola sobre lista enlazada puede hacer `push_back` y `pop_front` en O(1) si
103+
mantiene punteros a ambos extremos. El costo es una asignacion por nodo y peor
104+
localidad de memoria.
105+
106+
Una cola ingenua sobre vector puede insertar atras en O(1) amortizado, pero
107+
remover del frente cuesta O(n) porque desplaza elementos. Es facil de escribir y
108+
facil de medir como lenta.
109+
110+
Una cola circular usa memoria contigua, reutiliza ranuras y mantiene
111+
`enqueue/dequeue` en O(1) amortizado. La biblioteca estandar de Rust ofrece
112+
`VecDeque<T>` para este mismo patron de uso en produccion. Nuestra `Queue<T>` lo
113+
implementa con una API deliberadamente pequena para estudiar la representacion.
114+
115+
## Diagramas
116+
117+
El diagrama principal vive en [`diagrams/04-queue.mmd`](../diagrams/04-queue.mmd).
118+
119+
```mermaid
120+
flowchart LR
121+
title["Queue: FIFO con buffer circular"]
122+
123+
subgraph metadata["Metadatos"]
124+
head["head = 2"]
125+
len["len = 4"]
126+
cap["capacity = 6"]
127+
end
128+
129+
subgraph buffer["Buffer fisico"]
130+
slot0["0: D"]
131+
slot1["1: libre"]
132+
slot2["2: A (front)"]
133+
slot3["3: B"]
134+
slot4["4: C"]
135+
slot5["5: libre"]
136+
end
137+
138+
head --> slot2
139+
len --> slot0
140+
cap --> slot5
141+
142+
dequeue["dequeue()<br/>toma front y avanza head"]
143+
enqueue["enqueue(E)<br/>escribe en (head + len) % capacity"]
144+
145+
dequeue --> head
146+
enqueue --> slot1
147+
```
148+
149+
## Analisis de complejidad
150+
151+
| Operacion | Mejor caso | Caso promedio | Peor caso | Espacio |
152+
|-----------|------------|---------------|-----------|---------|
153+
| `new` | O(1) | O(1) | O(1) | O(1) |
154+
| `with_capacity(n)` | O(n) | O(n) | O(n) | O(n) |
155+
| `len` / `capacity` / `is_empty` | O(1) | O(1) | O(1) | O(1) |
156+
| `enqueue` | O(1) | O(1) amortizado | O(n) si crece | O(n) si crece |
157+
| `dequeue` | O(1) | O(1) | O(1) | O(1) |
158+
| `front` / `back` | O(1) | O(1) | O(1) | O(1) |
159+
| `clear` | O(n) | O(n) | O(n) | O(1) |
160+
| `iter` | O(1) crear, O(n) consumir | O(n) | O(n) | O(1) |
161+
162+
El peor caso de `enqueue` aparece cuando la cola esta llena y debe crecer. En
163+
ese crecimiento copiamos los valores en orden logico hacia un buffer nuevo con
164+
`head = 0`. Despues de crecer, el orden FIFO queda linealizado.
165+
166+
## Visualizacion interactiva (opcional)
167+
168+
No aplica todavia. La cola se entiende con el diagrama, los ejemplos de
169+
wraparound y los benchmarks; se agregara playground cuando `academy-web` tenga
170+
ese mecanismo definido.
171+
172+
## Implementacion
173+
174+
La implementacion vive en [`src/queue.rs`](../src/queue.rs).
175+
176+
El tipo guarda la representacion minima:
177+
178+
```rust
179+
pub struct Queue<T> {
180+
items: Box<[Option<T>]>,
181+
head: usize,
182+
len: usize,
183+
}
184+
```
185+
186+
`enqueue` calcula la ranura fisica donde termina la cola:
187+
188+
```rust
189+
let tail = self.physical_index(self.len);
190+
self.items[tail] = Some(value);
191+
self.len += 1;
192+
```
193+
194+
`dequeue` toma ownership del frente, limpia la ranura y avanza `head`:
195+
196+
```rust
197+
let value = self.items[self.head].take();
198+
self.head = self.physical_index(1);
199+
self.len -= 1;
200+
```
201+
202+
Cuando `len` llega a cero, `head` vuelve a `0`. Ese detalle no cambia la
203+
semantica, pero simplifica el estado interno y hace mas facil razonar sobre una
204+
cola recien vaciada.
205+
206+
El crecimiento es la parte mas delicada: no basta con copiar el arreglo fisico,
207+
porque el orden logico puede estar partido por el final del buffer. Por eso
208+
`grow` recorre `0..len`, calcula cada indice fisico viejo y deposita los valores
209+
en posiciones lineales del nuevo buffer.
210+
211+
## Pruebas
212+
213+
Las pruebas viven en [`tests/queue_test.rs`](../tests/queue_test.rs) y dentro de
214+
[`src/queue.rs`](../src/queue.rs).
215+
216+
Cubren:
217+
218+
- Underflow: `dequeue`, `front` y `back` sobre cola vacia.
219+
- Orden FIFO.
220+
- Reutilizacion de ranuras despues de `dequeue`.
221+
- Crecimiento despues de wraparound.
222+
- `clear` conservando capacidad.
223+
- Iteracion del frente al fondo.
224+
- Movimiento de ownership con `dequeue`.
225+
- Destruccion de valores restantes con `clear`.
226+
227+
Los doc-comments se validan con `cargo test --doc`.
228+
229+
## Benchmarks
230+
231+
El benchmark vive en [`benches/queue_bench.rs`](../benches/queue_bench.rs) y se
232+
ejecuta con:
233+
234+
```bash
235+
cargo bench --bench queue_bench
236+
```
237+
238+
Mide:
239+
240+
- `enqueue/dequeue` con buffer circular;
241+
- remocion ingenua del frente usando `Vector::remove(0)`;
242+
- reutilizacion de ranuras por wraparound.
243+
244+
La comparacion existe para que el costo de desplazar elementos deje de ser una
245+
idea abstracta. En una cola larga, remover del frente de un vector convierte una
246+
operacion conceptual simple en trabajo repetido sobre casi toda la coleccion.
247+
248+
## Ejercicios
249+
250+
### Ejercicio 1: Trazar orden FIFO `[Nivel 1]`
251+
252+
Ejecuta la secuencia `enqueue(A)`, `enqueue(B)`, `dequeue()`, `enqueue(C)`,
253+
`dequeue()`, `dequeue()` y registra los valores devueltos.
254+
255+
**Entrada/Salida esperada:** `[Some("A"), Some("B"), Some("C")]`.
256+
257+
<details>
258+
<summary>Pista</summary>
259+
`enqueue(C)` ocurre despues de remover `A`, pero `B` ya estaba esperando.
260+
</details>
261+
262+
### Ejercicio 2: Round-robin `[Nivel 2]`
263+
264+
Modela tareas con un contador de pasos restantes. En cada turno, remueve la
265+
tarea del frente, reduce su contador y vuelve a encolarla si aun no termina.
266+
267+
**Entrada/Salida esperada:** con `docs: 2` y `tests: 1`, el orden de terminacion
268+
es `["tests", "docs"]`.
269+
270+
<details>
271+
<summary>Pista</summary>
272+
Una tarea incompleta vuelve al fondo para dar oportunidad a las demas.
273+
</details>
274+
275+
### Ejercicio 3: Filtrar trabajos listos `[Nivel 3]`
276+
277+
Dada una cola de trabajos con bandera `ready`, consume la cola y devuelve solo
278+
los nombres de los trabajos listos en orden FIFO.
279+
280+
**Entrada/Salida esperada:** `index(true)`, `email(false)`, `report(true)`
281+
produce `["index", "report"]`.
282+
283+
<details>
284+
<summary>Pista</summary>
285+
`dequeue` transfiere ownership del trabajo; puedes decidir si conservarlo o
286+
descartarlo sin clonarlo.
287+
</details>
288+
289+
### Ejercicio 4: Backpressure en solicitudes `[Nivel 4]`
290+
291+
Disena una cola de solicitudes con limite de capacidad y una politica explicita
292+
cuando se llena: rechazar, esperar, soltar lo mas viejo o fusionar trabajos.
293+
Explica que invariante protege tu politica.
294+
295+
**Entrada/Salida esperada:** no hay una unica solucion; se evalua el diseno y
296+
la claridad de sus invariantes.
297+
298+
<details>
299+
<summary>Pista</summary>
300+
Una cola no solo ordena: tambien puede marcar el punto donde un sistema decide
301+
que hacer cuando recibe mas trabajo del que puede procesar.
302+
</details>
303+
304+
## Soluciones
305+
306+
Soluciones ejecutables de niveles 1 a 3:
307+
308+
- [`examples/soluciones/queue_trace_order.rs`](../examples/soluciones/queue_trace_order.rs)
309+
- [`examples/soluciones/queue_round_robin.rs`](../examples/soluciones/queue_round_robin.rs)
310+
- [`examples/soluciones/queue_filter_ready_jobs.rs`](../examples/soluciones/queue_filter_ready_jobs.rs)
311+
312+
Discusion para el nivel 4:
313+
314+
La politica de backpressure debe elegirse segun el dominio. Rechazar conserva
315+
memoria y latencia, pero puede perder trabajo. Esperar conserva solicitudes,
316+
pero aumenta latencia y puede bloquear productores. Soltar lo mas viejo sirve
317+
para eventos donde solo importa el estado reciente. Fusionar trabajos exige mas
318+
logica, pero puede reducir trabajo duplicado. La invariante importante es que el
319+
sistema no acepte trabajo ilimitado sin una decision explicita.
320+
321+
## Referencias
322+
323+
- Thomas H. Cormen, Charles E. Leiserson, Ronald L. Rivest, Clifford Stein,
324+
*Introduction to Algorithms*, secciones sobre colas y BFS.
325+
- Robert Sedgewick y Kevin Wayne, *Algorithms*, secciones introductorias de
326+
queues y recorridos FIFO.
327+
- Rust Standard Library, `VecDeque<T>`, como cola circular de produccion.
328+
- Rust Book, capitulos de ownership y borrowing, para entender por que
329+
`dequeue` transfiere valores.

docs/superpowers/plans/2026-07-14-rust-data-structures-course.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,11 @@ For each chapter, complete the following checklist before moving to the next str
201201
- Create: `examples/queue_advanced.rs`
202202
- Create: `examples/queue_real_case.rs`
203203

204-
- [ ] Teach FIFO, enqueue, dequeue, front, back, circular buffers, and why naive vector removal is costly.
205-
- [ ] Compare linked-list queue, circular-buffer queue, and standard `VecDeque`.
206-
- [ ] Include real cases: schedulers, BFS frontier, request processing.
207-
- [ ] Include tests for empty dequeue, FIFO order, wraparound behavior, growth, and mixed operations.
208-
- [ ] Include benchmarks contrasting naive front removal and circular-buffer behavior.
204+
- [x] Teach FIFO, enqueue, dequeue, front, back, circular buffers, and why naive vector removal is costly.
205+
- [x] Compare linked-list queue, circular-buffer queue, and standard `VecDeque`.
206+
- [x] Include real cases: schedulers, BFS frontier, request processing.
207+
- [x] Include tests for empty dequeue, FIFO order, wraparound behavior, growth, and mixed operations.
208+
- [x] Include benchmarks contrasting naive front removal and circular-buffer behavior.
209209

210210
### Task 9: Deque
211211

0 commit comments

Comments
 (0)