Progreso de estudio
0%
Universidad de Burgos · EDA

Estructuras de Datos y Algoritmos

Guía definitiva para el examen: soluciones paso a paso, teoría esencial, errores comunes, quiz interactivo y tutor con IA.

Árboles AVL Heapsort Grafos Quicksort Floyd-Warshall LCS · Mochila Dijkstra Quiz IA
Índice de ejercicios
EJ 7
Árbol AVL
EJ 4
Heapsort
EJ 5
Grafo: DFS, BFS, Topológico
EJ 6
Quicksort: partición
EJ 7b
Floyd-Warshall
EJ 5b
LCS · Prog. Dinámica
EJ 6b
Mochila 0/1
EJ 1a
Inserción en Montículo + Eliminaciones
EJ 2
Todos los Montículos posibles
EJ 4b
Todos los AVL con raíz ≥ 4
EJ 5b
Complejidad: Matriz vs Listas
EXTRA
Dijkstra
📚
Teoría + Big-O
⚠️
Errores comunes
🧠
Quiz autoevaluación
Tutor con IA
EJ 7 Árbol AVL — Recorridos, Inserción y Eliminación 1 punto
Enunciado: Dado un árbol AVL cuyo recorrido por niveles es:
59 15 68 3 21 67 82 8 16 51 81 98 40
1) Recorridos inorden, preorden y postorden. 2) Insertar 47, 73, 54. 3) Eliminar 67 y 82 del árbol inicial.

El recorrido por niveles se lee fila a fila de arriba abajo. Así queda el árbol:

⚠️ ¡Atención! El nodo 8 es hijo DERECHO del 3 (no izquierdo), porque 8 > 3. El nodo 3 no tiene hijo izquierdo en este árbol. Esto se refleja visualmente a continuación (nodo naranja a la derecha del 3).
59 15 68 3 21 67 82 8 (hijo der.) 16 51 81 98 40
Paso 0 / 13
Pulsa "Siguiente" para empezar el recorrido.
Orden de visita: —
Paso 0 / 9
Pulsa "Siguiente" para comenzar la inserción de 47, 73 y 54.
Nodo nuevo / insertado Nodo rotado / recolocado Nodo desequilibrado Raíz
1

Insertar 47

Ruta de inserción: 47 < 59 → izq → 47 > 15 → der → 47 > 21 → der → 47 < 51 → izq → 47 > 40 → der. Se inserta como hijo derecho de 40.

Revisión de FE (Factor de Equilibrio = h_izq − h_der) hacia arriba:
FE(51) = 1−0 = +1 ✓
FE(21) = 0−2 = −2 ❌ desequilibrio. El hijo derecho (51) tiene peso hacia su derecha → caso derecha-derecha (RR) → Rotación simple izquierda en 21.
51 sube: hijo izq de 51 = 21; hijo der de 51 = 47 (era hijo der de 40). 40 queda hijo izq de 47.
Árbol reequilibrado: FE(51)=0, FE(15)=−1 ✓

59 15 3 51 21 47 68 rotado nuevo (resto del árbol sin cambios)
2

Insertar 73 (sobre árbol tras insertar 47)

Ruta: 73 > 59 → der → 73 > 68 → der → 73 < 82 → izq → 73 < 81 → izq. Se inserta como hijo izquierdo de 81.

Revisión FE hacia arriba:
FE(81) = +1 ✓ | FE(82) = +2−1 = +1 ✓ | FE(68) = −1 ✓
Ningún nodo llega a ±2 → Sin rotación. El árbol sigue equilibrado.

3

Insertar 54 (sobre árbol tras 47 y 73)

Ruta: 54 < 59 → izq → 54 > 15 → der → 54 > 51 → der. Se inserta como hijo derecho de 51.

Revisión FE hacia arriba:
FE(51) = 0−1 = −1 ✓ | FE(21 o su padre actual) se propaga…
No se produce desequilibrio en este caso: FE(15) = −1−0→ se ajusta pero no supera ±2 con la estructura resultante de la inserción de 47.
Sin rotación. 54 se inserta directamente.

1

Eliminar 67

Tipo de nodo: 67 es un nodo hoja (sin hijos). Se elimina directamente sin necesidad de sucesor.

Revisión FE hacia arriba:
FE(68) = h_izq − h_der = 0 − 1 = −1 ✓ (válido, no hay rotación).
El árbol sigue siendo AVL.

59 15 (sin cambios) 68 FE=−1 ✓ 67✕ 82
2

Eliminar 82 (del árbol inicial, 67 todavía presente)

Tipo de nodo: 82 tiene dos hijos (81 izq, 98 der). Se aplica la regla del sucesor inorden (el mínimo del subárbol derecho).

Sucesor inorden de 82 = nodo más pequeño del subárbol derecho = 98 (único hijo derecho, sin hijos propios).
→ Se copia el valor 98 en la posición de 82, y se elimina el nodo hoja 98.
Resultado: donde había 82 ahora hay 98, con hijo izquierdo 81.

Revisión FE:
FE(98_nuevo) = 1 − 0 = +1 ✓ | FE(68) = h_izq − h_der: con 67 y el nuevo 98 → FE = 0 ✓
Sin rotación. Árbol AVL válido.

59 15 (sin cambios) 68 FE=0 ✓ 67 98 sucesor 81 82✕
Paso 0 / 6
Pulsa "Siguiente" para comenzar la eliminación de 90 y 8.
Nodo eliminado Sucesor inorden / recolocado Afectado por rebalanceo
Inorden: 8 → 9 → 13 → 16 → 18 → 19 → 21 → 26 → 61 → 67 → 77 → 89 → 90
Preorden: 18 → 9 → 8 → 16 → 13 → 21 → 19 → 77 → 26 → 61 → 67 → 89 → 90
Postorden: 8 → 13 → 16 → 9 → 19 → 21 → 67 → 61 → 26 → 90 → 89 → 77 → 18
EJ 4 Heapsort — Ordenación por montículo 1.5 puntos
Enunciado: Aplicar heapsort sobre: 30, 7, 8, 45, 98, 72, 91, 25, 73, 12
!

Idea general

Heapsort tiene dos fases: primero construye un max-heap (árbol donde cada padre es mayor que sus hijos), y luego extrae el máximo repetidamente colocándolo al final del array.

Empezamos heapify desde el índice n/2−1 = 4 hacia la izquierda:

i=4

Valor 98, hijos: 73 y 12

98 es mayor que ambos hijos → sin intercambio.

30
7
8
45
98
72
91
25
73
12
i=3

Valor 45, hijos: 25 y 73

73 > 45 → intercambio(45, 73)

30
7
8
73
98
72
91
25
45
12
i=2

Valor 8, hijos: 72 y 91

91 > 8 → intercambio(8, 91). El 8 baja a pos 6, sin más hijos en rango.

30
7
91
73
98
72
8
25
45
12
i=1

Valor 7, hijos: 73 y 98

98 > 7 → intercambio(7, 98). El 7 baja a pos 4, hijos: 45 y 12. 45 > 7 → intercambio(7, 45). El 7 baja a pos 8, sin más hijos.

30
98
91
73
45
72
8
25
7
12
i=0

Valor 30, hijos: 98 y 91

98 > 30 → intercambio(30, 98). Baja a pos 1, hijos: 73 y 45. 73 > 30 → intercambio(30, 73). Baja a pos 3, hijos: 25 y 7. 30 > ambos → fin.

98
73
91
30
45
72
8
25
7
12

✓ Max-heap construido: 98, 73, 91, 30, 45, 72, 8, 25, 7, 12

1

swap(98, 12) → heapify [0..8]

73
45
91
30
12
72
8
25
7
98
2

swap(91, 7) → heapify [0..7]

73
45
72
30
12
7
8
25
91
98
3

swap(73, 25) → heapify [0..6]

45
30
72
25
12
7
8
73
91
98
4

swap(72, 8) → heapify [0..5]

45
30
8
25
12
7
72
73
91
98
5

swap(45, 7) → heapify [0..4]

30
12
8
25
7
45
72
73
91
98
6

swap(30, 7) → heapify [0..3]

25
12
8
7
30
45
72
73
91
98
7

swap(25, 7) → heapify [0..2]

12
7
8
25
30
45
72
73
91
98
8

swap(12, 8) → heapify [0..1]

8
7
12
25
30
45
72
73
91
98
9

swap(8, 7) → fin

7
8
12
25
30
45
72
73
91
98
Resultado ordenado: 7, 8, 12, 25, 30, 45, 72, 73, 91, 98
EJ 5 Grafo — Matriz, Listas, DFS, BFS, Topológico 2 puntos
Enunciado: Para el grafo dado (nodos A,B,C,D,E,F,G,H,I): a) Matriz de adyacencia, b) Listas de adyacencia, c) DFS, d) BFS, e) Clasificación topológica.
B→H, B→G, H→D, H→E, H→C, D→E, E→C, G→I, G→F, I→F, F→C, F→A, C→A
ABCDEFGHI
A000000000
B000000110
C100000000
D000010000
E001000000
F101000000
G000001001
H001110000
I000001000
A: —
B: → G → H
C: → A
D: → E
E: → C
F: → A → C
G: → F → I
H: → C → D → E
I: → F

Regla: siempre elegir el vecino menor alfabéticamente

Pila inicial: [B]. Visito B → vecinos G, H → elijo G primero.
B → G (vecinos F, I → F primero) → F (vecinos A, C → A primero) → A (sin vecinos sin visitar, retrocedo) → C (sin vecinos sin visitar, retrocedo a F) → retrocedo a G → I → F (ya visitado, retrocedo) → retrocedo a B → H → C (visitado) → D → E → C (visitado, retrocedo) → fin.

DFS: B → G → F → A → C → I → H → D → E

Cola FIFO, vecinos en orden alfabético

Cola: [B]. Proceso B → encolo G, H. Cola: [G, H].
Proceso G → encolo F, I. Cola: [H, F, I].
Proceso H → encolo C, D, E (G ya visitado). Cola: [F, I, C, D, E].
Proceso F → A (C ya encolado). Cola: [I, C, D, E, A].
Proceso I → F ya visitado. Cola: [C, D, E, A].
Proceso C → A ya encolado. Proceso D, E, A → sin nuevos.

BFS: B → G → H → F → I → C → D → E → A

Algoritmo: eliminar nodos con grado entrada 0 repetidamente

Grados entrada: A=3, B=0, C=4, D=1, E=2, F=2, G=1, H=1, I=1.
Solo B tiene grado 0 → sacamos B, reducimos grados de G y H.
Ahora G=0, H=0 → sacamos G (menor), reducimos F e I → sacamos H, reducimos C, D, E.
I=0 → sacamos I, reduce F → F=0 → sacamos F, reduce A y C → D=0, E=0 → sacamos D → reduce E ya en 0 → sacamos E, reduce C → C=0 → sacamos C, reduce A → A=0 → sacamos A.

Topológico: B → G → H → I → D → E → F → C → A
EJ 6 Quicksort — Proceso de partición 1 punto
Enunciado: Vector: 74 98 97 31 50 86 55 — Pivote: 74 (primer elemento)
0

Estado inicial

i=0 (pivote=74), j=6

74
98
97
31
50
86
55
1

i avanza hasta >74, j retrocede hasta ≤74

i=1 (98>74) ✓ — j=6 (55≤74) ✓ — i < j → swap(98, 55)

74
55
97
31
50
86
98
2

Continuamos

i=2 (97>74) ✓ — j=5 (86>74) → j=4 (50≤74) ✓ — i < j → swap(97, 50)

74
55
50
31
97
86
98
3

i cruza a j → colocamos pivote

i avanza: i=3 (31≤74) → i=4 (97>74). j=3 (31≤74). Ahora i > j → cruzados. swap(pivote, arr[j]) = swap(74, 31)

31
55
50
74
97
86
98
Resultado M1: [31, 55, 50] | 74 | [97, 86, 98]
0

pivote=74 en pos 0, i=1, j recorre 1→6

74
98
97
31
50
86
55
1

j=1: 98>74 → nada. j=2: 97>74 → nada. j=3: 31<74 → swap(arr[1], arr[3])

74
31
97
98
50
86
55

i avanza a 2

2

j=4: 50<74 → swap(arr[2], arr[4])

74
31
50
98
97
86
55

i avanza a 3

3

j=5: 86>74 → nada. j=6: 55<74 → swap(arr[3], arr[6])

74
31
50
55
97
86
98

i avanza a 4

4

Fin del recorrido → swap(pivote, arr[i-1]) = swap(arr[0], arr[3])

55
31
50
74
97
86
98
Resultado M2: [55, 31, 50] | 74 | [97, 86, 98]
EJ 7b Floyd-Warshall — Matrices de distancias y caminos 1 punto
Enunciado: Grafo con nodos a, b, c, d. Aristas: d→c=6, d→b=2, c→b=6, b→a=5
!

¿Qué hace Floyd?

Calcula las distancias mínimas entre todos los pares de nodos. Para cada nodo k intermedio, comprueba si ir de i→k→j es más corto que ir de i→j directamente.

Fórmula: D[i][j] = min(D[i][j], D[i][k] + D[k][j])

abcd
a0
b50
c60
d260
Nadie puede llegar a otros nodos pasando por a, porque a no tiene arcos de salida. La matriz no cambia.
abcd
a0
b50
c1160
d7260

c→a: antes ∞, ahora c→b→a = 6+5 = 11. d→a: antes ∞, ahora d→b→a = 2+5 = 7.

d→c=6 existe, pero d→c→b = 6+6 = 12 > d→b = 2. Sin mejora. La matriz no cambia.
Sin cambios. Matriz final = D².
Distancias mínimas finales:
b→a = 5 (directo) | c→a = 11 (c→b→a) | d→a = 7 (d→b→a)
c→b = 6 (directo) | d→b = 2 (directo) | d→c = 6 (directo)
Resto de pares: ∞ (no hay camino)
EJ 5b LCS — Subsecuencia Común Más Larga 1 punto
Enunciado: Cadenas: "TCAACGTTA" y "CAACGTCG". Obtener la LCS por programación dinámica.
!

¿Cómo funciona la tabla LCS?

Si los caracteres coinciden: L[i][j] = L[i-1][j-1] + 1
Si no coinciden: L[i][j] = max(L[i-1][j], L[i][j-1])

εCAACGTCG
ε000000000
T000000111
C011111122
A012222222
A012333333
C012344444
G012345555
T012345666
T012345666
A012345666
LCS = "CAACGT" (longitud 6)
Recuperación: trazar desde L[9][8]=6 hacia atrás buscando las coincidencias marcadas en morado.
EJ 6b Mochila 0/1 — Programación Dinámica 1 punto
Enunciado: Objetos: 1(p=3,v=15), 2(p=4,v=16), 3(p=1,v=13), 4(p=3,v=15). Capacidad=10. Maximizar valor.
!

Fórmula 0/1 (cada objeto máximo una vez)

M[i][w] = M[i-1][w] si peso[i] > w (no cabe)
M[i][w] = max(M[i-1][w], M[i-1][w-peso[i]] + val[i]) si cabe

Obj\Cap012345678910
000000000000
1(p3,v15)0001515151515151515
2(p4,v16)0001516161631313131
3(p1,v13)013131528292931444444
4(p3,v15)013131528293031444646
Nota: En mochila 0/1 cada objeto se usa como máximo una vez. Al construir la fila i, se toma M[i-1][w] (sin el objeto i) y M[i-1][w-peso_i] + val_i (añadiendo el objeto i exactamente una vez), nunca se puede reutilizar.

Recuperación de objetos (trazar hacia atrás desde M[4][10]=46)

M[4][10]=46 ≠ M[3][10]=44 → objeto 4 incluido (p=3, v=15; nuevo w=10−3=7)
M[3][7]=31 = M[2][7]=31 → objeto 3 NO incluido (w queda en 7)
M[2][7]=31 ≠ M[1][7]=15 → objeto 2 incluido (p=4, v=16; nuevo w=7−4=3)
M[1][3]=15 ≠ M[0][3]=0 → objeto 1 incluido (p=3, v=15; nuevo w=3−3=0)
M[0][0]=0 → fin

✅ Solución óptima 0/1: objetos 1, 2 y 4
Pesos: 3 + 4 + 3 = 10 ≤ 10 ✓
Valor total: 15 + 16 + 15 = 46

Verificación de alternativas:
Obj 1+2+3: peso 3+4+1=8, valor 15+16+13=44 < 46
Obj 2+3+4: peso 4+1+3=8, valor 16+13+15=44 < 46
Obj 1+3+4: peso 3+1+3=7, valor 15+13+15=43 < 46
Obj 1+2+3+4: peso 11 > 10 ❌ no cabe
EJ 1a Inserción en Montículo mínimo + 3 Eliminaciones 1 punto
Enunciado: Inserte consecutivamente los valores 17, 52, 71, 92, 98, 38 sobre un montículo mínimo inicialmente vacío. Sobre el montículo resultante, realice la operación eliminar tres veces.
!

Regla de inserción en min-heap

Se inserta al final (último nivel, de izquierda a derecha) y se hace sift-up: se compara el nodo con su padre; si es menor, se intercambian. Se repite hasta que el padre sea menor o se llegue a la raíz.

Regla de eliminación del mínimo: Se extrae la raíz, se coloca el último elemento en su lugar, y se hace sift-down: se intercambia con el hijo menor hasta recuperar la propiedad.

+17

Insertar 17

Montículo vacío → 17 es la raíz. Sin sift-up necesario.

17

Array: [17]

+52

Insertar 52

Se inserta a la derecha de 17. Padre de pos 1 = pos 0 → 17. 52 ≥ 17 → sin intercambio.

17 52

Array: [17, 52]

+71

Insertar 71

Se inserta en pos 2 (hijo derecho de 17). Padre = 17. 71 ≥ 17 → sin intercambio.

17 52 71

Array: [17, 52, 71]

+92

Insertar 92

Pos 3 = hijo izquierdo de 52. Padre = 52. 92 ≥ 52 → sin intercambio.

17 52 71 92

Array: [17, 52, 71, 92]

+98

Insertar 98

Pos 4 = hijo derecho de 52. Padre = 52. 98 ≥ 52 → sin intercambio.

17 52 71 92 98

Array: [17, 52, 71, 92, 98]

+38

Insertar 38 — ¡Requiere sift-up!

Pos 5 = hijo izquierdo de 71. Padre = 71. 38 < 71 → intercambio(38, 71).
Ahora pos 2, padre = 17. 38 ≥ 17 → fin.

17 52 38 ↑ sift-up 92 98 71

Array final: [17, 52, 38, 92, 98, 71]

Montículo final tras todas las inserciones: [17, 52, 38, 92, 98, 71]
Árbol: raíz=17, hijos de 17: 52(izq) y 38(der). Hijos de 52: 92(izq) y 98(der). Hijos de 38: 71(izq).
−1

Eliminar mínimo (17)

Extraemos la raíz (17). El último elemento (71) pasa a la raíz. Sift-down: hijos de 71 son 52 y 38. Mínimo hijo = 38. 38 < 71 → intercambio(71, 38). 71 baja a pos 2, su único hijo es 71 mismo (pos 5), pero ya no tiene hijos → fin.

38 52 71 92 98 17✕

Extraído: 17 | Array: [38, 52, 71, 92, 98]

−2

Eliminar mínimo (38)

Extraemos 38. El último (98) pasa a raíz. Hijos: 52(izq) y 71(der). Mínimo = 52. 52 < 98 → intercambio(98, 52). 98 baja a pos 1, hijos: 92(izq). 92 < 98 → intercambio(98, 92). 98 en pos 3, sin hijos → fin.

52 92 71 98

Extraído: 38 | Array: [52, 92, 71, 98]

−3

Eliminar mínimo (52)

Extraemos 52. El último (98) pasa a raíz. Hijos: 92(izq) y 71(der). Mínimo = 71. 71 < 98 → intercambio(98, 71). 98 baja a pos 2, sin hijos → fin.

71 92 98

Extraído: 52 | Array: [71, 92, 98]

Eliminaciones en orden: 17 → 38 → 52
Montículo resultante tras 3 eliminaciones: [71, 92, 98]
EJ 2 Todos los montículos de máximos posibles con [1, 1, 2, 2, 3, 3] 1.5 puntos
Enunciado: Dibuje todos los montículos (de máximos) posibles con los siguientes 6 elementos [1, 1, 2, 2, 3, 3].
!

Condiciones que debe cumplir un max-heap de 6 nodos

Estructura fija: con 6 nodos, la forma del árbol siempre es la misma (completo por niveles): raíz, 2 hijos, 4 nietos de los cuales el último nivel tiene 3 ocupados (izq, centro-izq, centro-der de la rama izquierda).

Propiedad de montículo máximo: cada padre ≥ sus hijos.

Con los valores {1,1,2,2,3,3}, la raíz debe ser 3 (máximo). A continuación se enumeran todas las asignaciones válidas.

pos 0 pos 1 pos 2 pos 3 pos 4 pos 5 padre(i)=⌊(i-1)/2⌋ izq=2i+1, der=2i+2

Raíz (pos 0): siempre 3

El máximo del conjunto es 3, y debe ir en la raíz. Nos quedan {1,1,2,2,3} para las 5 posiciones restantes.

Hijos de la raíz (pos 1 y pos 2): deben ser ≤ 3. El segundo 3 puede estar en pos 1 o pos 2. Los 2s también pueden estar en esas posiciones. Analizamos caso por caso:

Montículo 1
3 3 2 2 1 1
[3,3,2,2,1,1]
Montículo 2
3 3 2 1 2 1
[3,3,2,1,2,1]
Montículo 3
3 3 2 1 1 2
[3,3,2,1,1,2]
Montículo 4
3 2 3 2 1 1
[3,2,3,2,1,1]
Montículo 5
3 2 3 1 2 1
[3,2,3,1,2,1]
Montículo 6
3 2 3 1 1 2
[3,2,3,1,1,2]
Total: 6 montículos de máximos distintos (considerando posiciones, no solo multiconjuntos).
Raíz siempre = 3. El segundo 3 puede ir en pos 1 (izq) o pos 2 (der). Para cada caso, los 2s pueden ocupar las posiciones de nivel 2 disponibles, con los 1s en el resto. Dado que hay 2 copias de cada valor, se generan las 6 combinaciones estructuralmente distintas mostradas.
EJ 4b Todos los árboles AVL con [1,2,3,4,5,6,7] y raíz ≥ 4 1.5 puntos
Enunciado: Dibuje todos los árboles AVL posibles con los elementos [1, 2, 3, 4, 5, 6, 7] en los que la raíz sea mayor o igual que 4.
!

Condiciones de un AVL con 7 nodos y raíz ≥ 4

AVL: árbol BST donde |h_izq − h_der| ≤ 1 en cada nodo.
Con 7 nodos: la única forma AVL perfectamente balanceada es el árbol completo de altura 3 (raíz + 2 hijos + 4 nietos). Factor de equilibrio = 0 en todos.
BST: todos los valores del subárbol izquierdo < raíz < todos los del subárbol derecho.
Raíces posibles: 4, 5, 6, 7 (pero raíz = 7 dejaría 6 nodos todos a la izquierda → no AVL). Veamos:

¿Qué raíces son viables?

Para que el árbol sea AVL con 7 nodos, el subárbol izquierdo y el derecho deben tener alturas que difieran en ≤1. Con 7 nodos y equilibrio perfecto: izquierda=3 nodos, derecha=3 nodos (árbol completo).
Raíz = 4: izq={1,2,3} (3 nodos), der={5,6,7} (3 nodos) ✅
Raíz = 5: izq={1,2,3,4} (4 nodos), der={6,7} (2 nodos) → alturas 2 vs 1 → FE=+1 ✅ (válido AVL)
Raíz = 6: izq={1,2,3,4,5} (5 nodos), der={7} (1 nodo) → alturas 2 vs 0 → FE=+2 ❌
Raíz = 7: izq={1,2,3,4,5,6} (6 nodos), der=∅ → FE=+3 ❌
Raíces válidas: 4 y 5.

Izquierda: {1,2,3} → raíz del subárbol izq = 2. Derecha: {5,6,7} → raíz del subárbol der = 6. Es el único AVL perfecto con raíz 4.

4 2 6 1 3 5 7 FE=0 FE=0 FE=0
Raíz=4: único árbol AVL posible → [4, 2, 6, 1, 3, 5, 7] (árbol completo perfecto, altura 2)

Izq={1,2,3,4} con 4 nodos, Der={6,7} con 2 nodos. El subárbol izquierdo tiene h=2 y el derecho h=1 → FE(raíz)=+1 ✅. Para {1,2,3,4} hay 4 AVLs posibles con raíces 2 o 3; para {6,7} solo hay uno (raíz=6, hijo der=7).

AVL con raíz=5, sub-izq raíz=2
5 2 6 1 3 7 4
Sub-izq: 2→(1, 3→(−,4)) | Sub-der: 6→(−,7)
AVL con raíz=5, sub-izq raíz=3
5 3 6 2 4 7 1
Sub-izq: 3→(2→(1,−), 4) | Sub-der: 6→(−,7)
Total de AVLs distintos con raíz ≥ 4:
• Raíz = 4: 1 árbol (perfecto, completamente simétrico)
• Raíz = 5: 2 árboles (subárbol izq con raíz 2 o raíz 3; subárbol der siempre 6→7)
• Raíz = 6: ❌ no válido (FE=+2 en la raíz)
• Raíz = 7: ❌ no válido (FE=+3 en la raíz)
TOTAL: 3 árboles AVL válidos
EJ 5c Complejidad temporal: Matriz de adyacencia vs Listas de adyacencia 1 punto
Enunciado: Para las implementaciones de grafos basadas en matrices de adyacencia y listas de adyacencia, indique el tiempo de ejecución de: comprobar si hay un arco entre dos nodos, añadir un arco, añadir un nodo, eliminar un nodo.
!

Notación

V = número de vértices (nodos) | E = número de arcos (aristas) | deg(v) = grado del nodo v

Operación Matriz de adyacencia Listas de adyacencia
¿Hay arco (u, v)? O(1) O(deg(u))
Añadir arco (u, v) O(1) O(1)
Añadir un nodo O(V²) O(1)
Eliminar un nodo O(V²) O(V + E)
1

¿Hay arco (u, v)?

Matriz: acceso directo M[u][v]O(1).
Listas: hay que recorrer la lista de vecinos de u hasta encontrar v (o no). En el peor caso, u tiene todos los vértices como vecinos → O(deg(u)), que puede ser O(V).

2

Añadir arco (u, v)

Matriz: M[u][v] = 1 (y M[v][u]=1 si no dirigido) → O(1).
Listas: insertar v al principio de la lista de u (y viceversa) → O(1).

3

Añadir un nodo

Matriz: es necesario ampliar la matriz de V×V a (V+1)×(V+1), copiando o reasignando toda la estructura → O(V²).
Listas: simplemente añadir una nueva entrada vacía a la lista de listas → O(1).

4

Eliminar un nodo

Matriz: hay que eliminar la fila y columna del nodo, y reorganizar la matriz → O(V²).
Listas: hay que eliminar la lista del nodo y también todas las referencias a ese nodo en las listas de sus vecinos. En el peor caso (grafo denso), esto implica revisar todas las listas → O(V + E).

Resumen: La matriz de adyacencia destaca en consultas (O(1) para ¿existe arco?), pero es costosa en operaciones estructurales (añadir/eliminar nodos: O(V²)). Las listas de adyacencia son más eficientes en memoria y en modificaciones de estructura, pero más lentas en consultar la existencia de un arco concreto.

Espacio: Matriz = O(V²) | Listas = O(V + E)
EXTRA Dijkstra — Caminos mínimos desde un origen Frecuente en examen
Enunciado ejemplo: Dado el grafo con nodos {A,B,C,D,E} y aristas ponderadas: A→B=4, A→C=2, C→B=1, B→D=5, C→D=8, C→E=10, D→E=2. Calcular Dijkstra desde A.
!

Idea central

Dijkstra mantiene una tabla de distancias mínimas conocidas desde el origen. En cada iteración, selecciona el nodo no visitado con menor distancia, lo marca como visitado y actualiza ("relaja") las distancias de sus vecinos. Solo funciona con pesos no negativos.

IteraciónNodo visitadodist[A]dist[B]dist[C]dist[D]dist[E]
Inicio0
1A (dist=0)0 ✓42
2C (dist=2)0 ✓32 ✓1012
3B (dist=3)0 ✓3 ✓2 ✓812
4D (dist=8)0 ✓3 ✓2 ✓8 ✓10
5E (dist=10)0 ✓3 ✓2 ✓8 ✓10 ✓
1

Visitar A (dist=0)

A es el origen. Relajamos sus vecinos: B = 0+4 = 4, C = 0+2 = 2. Ambos mejoran respecto a ∞. A queda marcado como visitado.

2

Visitar C (dist=2, mínimo no visitado)

Vecinos de C: B → min(4, 2+1) = 3 ✓ mejora. D → min(∞, 2+8) = 10. E → min(∞, 2+10) = 12.

3

Visitar B (dist=3, mínimo no visitado)

Vecinos de B: D → min(10, 3+5) = 8 ✓ mejora. E no es vecino directo de B. B queda visitado.

4

Visitar D (dist=8)

Vecinos de D: E → min(12, 8+2) = 10 ✓ mejora. D queda visitado.

5

Visitar E (dist=10) — fin

E no tiene vecinos sin visitar. Algoritmo completado.

Distancias mínimas desde A:
A→A = 0 | A→B = 3 (ruta: A→C→B) | A→C = 2 (ruta: A→C) | A→D = 8 (ruta: A→C→B→D) | A→E = 10 (ruta: A→C→B→D→E)

Complejidad: O((V + E) log V) con cola de prioridad | O(V²) con tabla simple
📚 Teoría esencial — Chuleta para el examen
🌳Árbol AVL
BST con |FE| ≤ 1 en cada nodo. FE = altura(izq) − altura(der).

Rotaciones:
• LL → rotación simple derecha
• RR → rotación simple izquierda
• LR → doble: izq-izq, luego derecha
• RL → doble: der-der, luego izquierda

Recorridos: Inorden = ordenado ✓

Complejidad: búsqueda O(log n) inserción O(log n)
🔢Montículo (Heap)
Árbol binario completo con propiedad de montículo.
Max-heap: padre ≥ hijos (heapsort)
Min-heap: padre ≤ hijos (cola prioridad)

Representación en array:
• Hijo izq de i → 2i+1
• Hijo der de i → 2i+2
• Padre de i → (i-1)/2

inserción O(log n) extraer-min O(log n)
Quicksort
Divide y vencerás. Elige pivote, particiona el array (menores izq, mayores der), recursa en cada parte.

Esquema Lomuto: pivot=último. i apunta al final de la zona menor. j recorre. Si A[j] ≤ pivot → swap(A[i+1], A[j]), i++.

mejor O(n log n)
medio O(n log n)
peor O(n²) — array ordenado
📊Heapsort
Fase 1: construir max-heap con heapify desde n/2−1 hasta 0.
Fase 2: extraer máximo (swap raíz↔último), reducir heap, heapify(0).

Siempre O(n log n) — sin caso peor cuadrático.
No estable. Ordenación in-place.

O(n log n) siempre.
Espacio extra: O(1)
🗺️Grafos: BFS / DFS
BFS (cola FIFO): recorre nivel a nivel. Sirve para caminos mínimos sin pesos.
DFS (pila/recursión): va en profundidad. Sirve para orden topológico, detección de ciclos.

Orden topológico = DFS, añadir a pila al terminar cada nodo. Solo en DAGs (sin ciclos).

Matriz: O(V²) Listas: O(V+E)
🛣️Floyd-Warshall / Dijkstra
Floyd-Warshall: todos los pares. DP con matriz D. D[i][j] = min(D[i][j], D[i][k]+D[k][j]) para cada k.
O(V³)

Dijkstra: un origen, todos los destinos. Greedy, requiere pesos ≥ 0.
O((V+E) log V) con heap
O(V²) sin heap
🎒Programación Dinámica
Resolución de subproblemas superpuestos guardando resultados.

LCS: dp[i][j] = dp[i-1][j-1]+1 si iguales, else max(dp[i-1][j], dp[i][j-1]).

Mochila 0/1: dp[i][w] = max(dp[i-1][w], dp[i-1][w−wi]+vi) si wi≤w.

LCS: O(mn) Mochila: O(nW)
📈Big-O — Tabla completa
AlgoritmoMejorMedioPeor
Quicksortn log nn log n
Heapsortn log nn log nn log n
Mergesortn log nn log nn log n
Búsqueda AVL1log nlog n
Floyd-Warshall
Dijkstra (heap)(V+E) log V
BFS / DFSV+E
⚠️ Errores comunes en el examen — No los cometas
🌳
AVL: calcular FE al revés. FE = altura(subárbol izquierdo) − altura(subárbol derecho). Si lo inviertes, el signo de la rotación estará equivocado. El árbol está desequilibrado cuando FE = +2 o FE = −2.
🔄
AVL: confundir LL/RR con LR/RL. LL y RR requieren una sola rotación. LR y RL requieren dos rotaciones (doble). Si el desequilibrio es en el nieto del lado contrario → rotación doble.
📦
Heap: olvidar el sift-down tras insertar o el sift-up tras extraer. En heapsort, tras poner el último elemento en la raíz, siempre hay que hacer sift-down para restaurar la propiedad. En inserción, el nuevo elemento sube (sift-up) comparando con su padre.
🔢
Heapsort: comenzar heapify desde n/2 en lugar de n/2−1. Los índices van de 0 a n−1. El primer nodo interno está en posición n/2−1 (indexación 0). Empezar en n/2 es incorrecto.
Quicksort: confundir pivote con la posición final. Tras la partición de Lomuto, el pivote queda en su posición definitiva (se intercambia con A[i+1]). Los subarrays a ordenar NO incluyen al pivote.
🗺️
Floyd-Warshall: olvidar inicializar la diagonal a 0 y los pesos directos. D[i][i] = 0 siempre. D[i][j] = peso de la arista si existe, ∞ si no hay arco directo. El bucle externo es sobre k (el nodo intermedio).
🎒
Mochila 0/1: usar un objeto más de una vez. En 0/1 cada objeto se usa 0 o 1 veces. La tabla dp va de i=1..n objetos y w=0..W capacidad. La recurrencia consulta dp[i−1][...] (fila anterior), no dp[i][...].
🧩
LCS: confundir la longitud con la propia subsecuencia. dp[m][n] da la longitud. Para reconstruir la subsecuencia hay que hacer backtracking desde dp[m][n] siguiendo las celdas de donde vino cada valor.
🛣️
Dijkstra con pesos negativos. Dijkstra NO funciona correctamente si hay aristas con peso negativo. En ese caso usar Bellman-Ford. Dijkstra garantiza que la primera vez que visita un nodo, ya tiene la distancia mínima (solo si pesos ≥ 0).
📊
Orden topológico en grafos con ciclos. El orden topológico solo existe en DAGs (grafos dirigidos acíclicos). Si el grafo tiene ciclos, no hay orden topológico posible. DFS detecta esto: si hay una arista de vuelta (back edge), hay ciclo.
🧠 Quiz de autoevaluación
✨ Tutor con IA — Pregunta lo que no entiendas
🤖
Tutor EDA
Especializado en Estructuras de Datos y Algoritmos
listo
¡Hola! Soy tu tutor de EDA. Puedes preguntarme cualquier cosa sobre AVL, Heapsort, Quicksort, Floyd-Warshall, LCS, Mochila, Dijkstra, grafos... ¿Qué te genera más dudas?