Generado con IA · Supuesto práctico

Estructuras de datos avanzadas: árbol AVL para el índice de un repositorio

Caso único con preguntas Informática Comunidad Valenciana
Descargar:
Supuesto práctico generado por la IA de OposicionesIA, con su orientación y solución modelo más abajo. Dentro de la plataforma, además, puedes resolverlo y recibir una corrección con rúbrica y nota.

Estructuras de datos avanzadas: árbol AVL para el índice de un repositorio

Contexto

En el módulo Programación (0485) del ciclo DAW del IES «La Marxadella» (Comunitat Valenciana), se estudia cómo mantener ordenado y equilibrado un índice de recursos. Cada recurso se identifica por una clave entera (su id) y se quiere garantizar búsquedas, inserciones y borrados eficientes incluso si los id llegan casi ordenados (caso que degrada un BST clásico). Se opta por un árbol AVL, un árbol binario de búsqueda autoequilibrado en el que, para todo nodo, el factor de equilibrio FE = altura(subárbol izq) − altura(subárbol der) cumple FE ∈ {−1, 0, +1}.

Se parte de un árbol vacío y se insertan, en este orden, las claves:

10, 20, 30, 40, 50, 25

Cuestiones

1. Construcción con rotaciones. Inserta las seis claves una a una. Tras cada inserción que rompa el equilibrio, indica el nodo que se desequilibra, su factor de equilibrio, el tipo de rotación aplicada (LL/simple-derecha, RR/simple-izquierda, LR o RL) y dibuja el árbol resultante. Muestra el árbol final con el factor de equilibrio de cada nodo. Comprueba que su altura es la mínima posible para 6 claves.

2. Por qué AVL y no BST. Demuestra qué forma tendría el árbol si se insertasen las claves 10, 20, 30, 40, 50 en un BST sin equilibrar y razona cuál sería su altura y el coste de búsqueda resultante. Explica cómo el AVL garantiza una altura O(log n) y, por tanto, búsqueda, inserción y borrado en O(log n), frente al O(n) del peor caso del BST. ¿Qué información extra debe guardar cada nodo AVL para poder recalcular el equilibrio?

3. Borrado. Sobre el árbol final de la cuestión 1, elimina la clave 30. Detalla: cómo se elige el sustituto (predecesor o sucesor in-orden), qué subárboles se ven afectados, si el borrado provoca un desequilibrio y, en caso afirmativo, qué rotación lo corrige. Dibuja el árbol resultante. Recuerda que, a diferencia de la inserción, un borrado puede requerir propagar reequilibrados hacia la raíz.

4. Esquema de la rotación. Escribe el pseudocódigo (o Java) de la rotación simple a la derecha rotarDerecha(nodo), que recibe el nodo desequilibrado y devuelve la nueva raíz del subárbol. Indica explícitamente: qué punteros se reasignan, en qué orden, y dónde se recalculan las alturas. Explica luego, en una frase, cómo se compone una rotación LR a partir de las rotaciones simples.

5. Comparación con tabla hash. El equipo se plantea si, en lugar del AVL, no sería mejor una tabla hash. Compara ambas estructuras para este índice de recursos respondiendo: (a) complejidad media y en el peor caso de la búsqueda por clave en cada una; (b) qué operación ofrece el AVL «gratis» que una tabla hash no soporta de forma eficiente (recorrido ordenado y consultas por rango de id); (c) qué es el factor de carga de una tabla hash y cómo afecta a las colisiones. Concluye cuál elegirías si el requisito principal es listar los recursos ordenados por id.

Orientación cómo resolverlo por tu cuenta

Cuestión 1 (construcción). Recuerda el invariante AVL: FE ∈ {−1,0,+1}. Inserta como en un BST normal y, al subir hacia la raíz, recalcula alturas y FE. El primer nodo desequilibrado (FE = ±2) más cercano a la hoja insertada es el que rotas. Identifica el caso por el camino de inserción: dos pasos a la izquierda → LL (rotación simple a la derecha), dos a la derecha → RR (simple izquierda), izquierda-derecha → LR, derecha-izquierda → RL. Error típico: rotar el nodo equivocado (debe ser el desequilibrado más bajo) o confundir LR con LL. Con claves crecientes verás desequilibrios repetidos; al final comprueba que la altura es la mínima para 6 nodos (recuerda que un árbol completo de altura h almacena hasta 2^(h+1)−1 claves).

Cuestión 2 (AVL vs BST). Insertar claves ya ordenadas en un BST genera una «lista enlazada» degenerada. Razona altura y coste de búsqueda en ese caso patológico y contrástalo con la cota logarítmica garantizada por el equilibrio AVL. Menciona qué dato extra guarda cada nodo (altura o el propio FE) para recalcular el equilibrio en O(1).

Cuestión 3 (borrado). Para borrar un nodo con dos hijos, sustituye por predecesor o sucesor in-orden. Tras eliminar físicamente, reequilibra subiendo: a diferencia de la inserción, el borrado puede exigir varias rotaciones en cadena hasta la raíz. No olvides recalcular alturas tras cada paso.

Cuestión 4 (rotación). Para rotarDerecha, piensa en tres punteros (raíz, su hijo izquierdo, y el subárbol derecho de ese hijo). Reasigna en orden seguro para no perder referencias y recalcula alturas de abajo arriba (primero el antiguo, luego el nuevo padre). LR = una rotación sobre el hijo seguida de otra sobre el nodo.

Cuestión 5 (hash). Compara complejidad media/peor caso, destaca recorrido ordenado y consultas por rango como ventaja del AVL, define factor de carga y su relación con colisiones, y concluye según el requisito de listar ordenado por id.

Solución modelo respuesta completa — intenta resolverlo antes de mirar

Cuestión 1. Construcción con rotaciones

Insertamos 10, 20, 30, 40, 50, 25 partiendo del árbol vacío. Notación: cada nodo se anota como clave(FE), con FE = altura(izq) − altura(der) y altura de un subárbol vacío = −1, hoja = 0.

Inserción de 10 → raíz.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 160 70" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <circle cx="80" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="80" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> </svg>

Inserción de 20 → a la derecha de 10.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 220 150" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="100" y1="55" x2="150" y2="105" stroke="#334155" stroke-width="1.5"/> <circle cx="100" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="100" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(-1)</text> <circle cx="160" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="160" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> </svg>
FE(10) = −1, equilibrado. Sin rotación.

Inserción de 30 → a la derecha de 20. Antes de rotar:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="100" y1="55" x2="150" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="160" y1="135" x2="210" y2="185" stroke="#334155" stroke-width="1.5"/> <circle cx="100" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="100" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(-2)</text> <circle cx="160" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="160" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-1)</text> <circle cx="220" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="220" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> </svg>
El nodo desequilibrado más bajo es 10 con FE = −2; el camino de inserción fue derecha-derecha → caso RR → rotación simple a la izquierda sobre 10. 20 sube a raíz:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 280 150" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="140" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="160" y1="55" x2="220" y2="105" stroke="#334155" stroke-width="1.5"/> <circle cx="150" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="150" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="230" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="230" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> </svg>

Inserción de 40 → a la derecha de 30.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 340 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="140" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="160" y1="55" x2="220" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="240" y1="135" x2="290" y2="185" stroke="#334155" stroke-width="1.5"/> <circle cx="150" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="150" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-1)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="230" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="230" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(-1)</text> <circle cx="300" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="300" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(0)</text> </svg>
FE(30) = −1, FE(20) = −1. Equilibrado, sin rotación.

Inserción de 50 → a la derecha de 40. Antes de rotar:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 310" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="140" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="160" y1="55" x2="220" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="240" y1="135" x2="290" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="310" y1="215" x2="350" y2="265" stroke="#334155" stroke-width="1.5"/> <circle cx="150" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="150" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-2)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="230" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="230" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(-2)</text> <circle cx="300" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="300" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(-1)</text> <circle cx="360" cy="275" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="360" y="275" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>
El desequilibrado más bajo es 30 (FE = −2), camino derecha-derecha → caso RR → rotación simple a la izquierda sobre 30. 40 sube ocupando el lugar de 30:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="140" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="160" y1="55" x2="290" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="290" y1="135" x2="240" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="310" y1="135" x2="360" y2="185" stroke="#334155" stroke-width="1.5"/> <circle cx="150" cy="35" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="150" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-1)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="300" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="300" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(0)</text> <circle cx="230" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="230" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> <circle cx="370" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="370" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>
Tras la rotación, 20 queda con FE = −1: equilibrado. (Regla AVL: en inserción, una sola rotación restablece el equilibrio de todo el árbol).

Inserción de 25 → BST: 25 > 20 → derecha; 25 < 40 → izquierda; 25 < 30 → izquierda de 30. Antes de rotar:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 310" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="150" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="170" y1="55" x2="290" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="290" y1="135" x2="240" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="310" y1="135" x2="370" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="220" y1="215" x2="180" y2="265" stroke="#334155" stroke-width="1.5"/> <circle cx="160" cy="35" r="22" fill="#fef2f2" stroke="#dc2626" stroke-width="2"/> <text x="160" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-2)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="300" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="300" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(+1)</text> <circle cx="230" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="230" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(+1)</text> <circle cx="380" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="380" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> <circle cx="170" cy="275" r="22" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <text x="170" y="275" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> </svg>
Recalculamos FE camino arriba: FE(30) = +1 y FE(40) = +1 siguen dentro del invariante, pero FE(20) = altura(10) − altura(40) = 0 − 2 = −2. El desequilibrado más bajo (y único) es 20. El camino desde 20 hacia la zona de inserción fue derecha-luego-izquierda (20→40 a la derecha, 40→30 a la izquierda) → caso RL → rotación doble: primero, rotación simple a la derecha sobre el hijo derecho (40), en la que 30 sube y 40 baja a su derecha:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 470 310" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="150" y1="55" x2="80" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="170" y1="55" x2="290" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="290" y1="135" x2="180" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="310" y1="135" x2="360" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="370" y1="215" x2="410" y2="265" stroke="#334155" stroke-width="1.5"/> <circle cx="160" cy="35" r="22" fill="#fef2f2" stroke="#dc2626" stroke-width="2"/> <text x="160" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(-2)</text> <circle cx="70" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="300" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="300" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(-1)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> <circle cx="370" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="370" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(-1)</text> <circle cx="420" cy="275" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="420" y="275" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>
Ojo: este es solo el paso intermedio — 20 sigue con FE = −2, pero el camino ha quedado derecha-derecha (caso RR). Segunda mitad de la doble rotación: rotación simple a la izquierda sobre 20. 30 sube a la raíz:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="210" y1="50" x2="130" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="230" y1="50" x2="310" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="110" y1="130" x2="80" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="130" y1="130" x2="160" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="330" y1="130" x2="380" y2="180" stroke="#334155" stroke-width="1.5"/> <circle cx="220" cy="35" r="22" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <text x="220" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> <circle cx="120" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="120" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="320" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="320" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(-1)</text> <circle cx="70" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> <circle cx="390" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="390" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>
Comprobación de FE tras la doble rotación:

  • FE(10) = 0, FE(25) = 0, FE(50) = 0 (hojas)
  • FE(20) = altura(10) − altura(25) = 0 − 0 = 0
  • FE(40) = altura(∅) − altura(50) = −1 − 0 = −1
  • FE(30) = altura(20) − altura(40) = 1 − 1 = 0

Todos los FE ∈ {−1, 0}, árbol AVL válido.

Árbol final con factores de equilibrio:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="210" y1="50" x2="130" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="230" y1="50" x2="310" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="110" y1="130" x2="80" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="130" y1="130" x2="160" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="330" y1="130" x2="380" y2="180" stroke="#334155" stroke-width="1.5"/> <circle cx="220" cy="35" r="22" fill="#f8fafc" stroke="#2563eb" stroke-width="2"/> <text x="220" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> <circle cx="120" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="120" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="320" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="320" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(-1)</text> <circle cx="70" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> <circle cx="390" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="390" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>

Altura mínima. El árbol tiene altura h = 2 (contando aristas: por ejemplo 30→40→50). Es la mínima posible para 6 claves: un árbol completo de altura 2 (3 niveles) almacena hasta 2^3 − 1 = 7 ≥ 6 nodos, mientras que uno de altura 1 solo aloja 3. Comprobado: nuestro AVL alcanza la altura mínima.

Como apunte de nivel tribunal: el AVL no garantiza siempre la altura mínima absoluta, sino la cota h ≤ 1,44·log₂(n+2) (árboles de Fibonacci). En este caso concreto el resultado sí coincide con el mínimo teórico y, en general, la altura queda siempre en O(log n).

Cuestión 2. Por qué AVL y no BST

Forma del BST sin equilibrar con 10, 20, 30, 40, 50. Como cada clave es mayor que todas las anteriores, siempre se inserta como hijo derecho del último nodo. El BST degenera en una lista enlazada inclinada a la derecha:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 390" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="80" y1="55" x2="130" y2="105" stroke="#334155" stroke-width="1.5"/> <line x1="150" y1="135" x2="200" y2="185" stroke="#334155" stroke-width="1.5"/> <line x1="220" y1="215" x2="270" y2="265" stroke="#334155" stroke-width="1.5"/> <line x1="290" y1="295" x2="340" y2="345" stroke="#334155" stroke-width="1.5"/> <circle cx="70" cy="35" r="22" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="70" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10</text> <circle cx="140" cy="115" r="22" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="140" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20</text> <circle cx="210" cy="195" r="22" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="210" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30</text> <circle cx="280" cy="275" r="22" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="280" y="275" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40</text> <circle cx="350" cy="355" r="22" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="350" y="355" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50</text> </svg>
  • Altura: h = n − 1 = 4 aristas (5 niveles). En general, para n claves ordenadas, h = n − 1.
  • Coste de búsqueda: buscar la clave 50 (o una inexistente mayor) recorre los n nodos → O(n). La búsqueda binaria se ha perdido por completo; el BST se comporta como una búsqueda secuencial.

Garantía O(log n) del AVL. El invariante FE ∈ {−1,0,+1} mantiene la altura acotada. Se demuestra con los números de Fibonacci: el AVL con menos nodos para una altura h tiene N(h) = N(h−1) + N(h−2) + 1 nodos (árbol de Fibonacci). De ahí se deduce:

h \le 1{,}4404 \cdot \log_2(n + 2) - 0{,}328 \approx O(\log n)

Como búsqueda, inserción y borrado recorren un único camino raíz→hoja y solo aplican un número constante de rotaciones por nivel afectado, su coste es proporcional a la altura → O(log n) en todos los casos, frente al O(n) del peor caso del BST.

Información extra en cada nodo. Cada nodo AVL debe almacenar, además de clave e hijos, su altura (o, equivalentemente, su factor de equilibrio). Con la altura de los hijos se recalcula en O(1):

\text{altura}(nodo) = 1 + \max\big(\text{altura}(izq),\ \text{altura}(der)\big) FE(nodo) = \text{altura}(izq) - \text{altura}(der)

Mantener la altura/FE actualizado tras cada inserción o borrado es lo que permite detectar el desequilibrio y decidir la rotación sin recorrer el subárbol completo.

Cuestión 3. Borrado de la clave 30

Partimos del árbol final de la cuestión 1:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 440 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="210" y1="50" x2="130" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="230" y1="50" x2="310" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="110" y1="130" x2="80" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="130" y1="130" x2="160" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="330" y1="130" x2="380" y2="180" stroke="#334155" stroke-width="1.5"/> <circle cx="220" cy="35" r="22" fill="#f8fafc" stroke="#2563eb" stroke-width="2"/> <text x="220" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">30(0)</text> <circle cx="120" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="120" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="320" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="320" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(-1)</text> <circle cx="70" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> <circle cx="390" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="390" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> </svg>

Paso 1 — Localizar y elegir sustituto. El nodo 30 es ahora la raíz y tiene dos hijos (el subárbol 20-10-25 y la rama 40-50). En un nodo con dos hijos se sustituye por el predecesor in-orden (máximo del subárbol izquierdo, aquí el 25) o el sucesor in-orden (mínimo del subárbol derecho). Tomamos el sucesor in-orden: el mínimo del subárbol derecho de 30 es 40 (40 no tiene hijo izquierdo). Copiamos la clave 40 en la raíz y eliminamos el nodo 40 original, reenganchando su hijo derecho (50) en su lugar.

Subárboles afectados: el subárbol derecho (la rama 40-50). Tras copiar 40 arriba, 50 pasa a colgar directamente de la nueva raíz:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="210" y1="50" x2="130" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="230" y1="50" x2="310" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="110" y1="130" x2="80" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="130" y1="130" x2="160" y2="180" stroke="#334155" stroke-width="1.5"/> <circle cx="220" cy="35" r="22" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <text x="220" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(?)</text> <circle cx="120" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="120" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="320" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="320" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> <circle cx="70" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> </svg>

Paso 2 — Recalcular alturas y FE subiendo hacia la raíz.

  • FE(10) = 0, FE(25) = 0, FE(50) = 0 (hojas).
  • FE(20) = altura(10) − altura(25) = 0 − 0 = 0 → equilibrado.
  • FE(40) (la nueva raíz) = altura(20) − altura(50) = 1 − 0 = +1 → equilibrado.

Paso 3 — ¿Desequilibrio? No. Todos los FE quedan en {−1, 0, +1}. El borrado no provoca desequilibrio, por lo que no se requiere ninguna rotación en este caso.

Árbol resultante:

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 420 230" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <line x1="210" y1="50" x2="130" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="230" y1="50" x2="310" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="110" y1="130" x2="80" y2="180" stroke="#334155" stroke-width="1.5"/> <line x1="130" y1="130" x2="160" y2="180" stroke="#334155" stroke-width="1.5"/> <circle cx="220" cy="35" r="22" fill="#f8fafc" stroke="#2563eb" stroke-width="2"/> <text x="220" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">40(+1)</text> <circle cx="120" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="120" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">20(0)</text> <circle cx="320" cy="115" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="320" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">50(0)</text> <circle cx="70" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="70" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">10(0)</text> <circle cx="170" cy="195" r="22" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="170" y="195" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">25(0)</text> </svg>

Nota sobre propagación. A diferencia de la inserción (donde una única rotación restablece todo el árbol), en el borrado una rotación puede reducir la altura del subárbol y desequilibrar a un ancestro, obligando a propagar el reequilibrado hacia la raíz (potencialmente O(log n) rotaciones). En este supuesto concreto no llega a producirse, pero el algoritmo general debe seguir comprobando el FE en cada ancestro hasta la raíz.

Cuestión 4. Esquema de la rotación simple a la derecha

La rotación simple a la derecha se aplica al caso LL (el subárbol izquierdo es el más alto y el desequilibrio viene por la izquierda del hijo izquierdo). El hijo izquierdo h sube a ocupar el lugar del nodo desequilibrado z, que baja a la derecha; el antiguo subárbol derecho de h (T2) se reengancha como subárbol izquierdo de z.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 620 240" font-family="ui-sans-serif, system-ui, Arial, sans-serif"> <!-- Árbol izquierdo: antes --> <line x1="95" y1="50" x2="65" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="115" y1="50" x2="150" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="55" y1="125" x2="30" y2="175" stroke="#334155" stroke-width="1.5"/> <line x1="75" y1="125" x2="100" y2="175" stroke="#334155" stroke-width="1.5"/> <circle cx="105" cy="35" r="18" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="105" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">z</text> <circle cx="65" cy="115" r="18" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="65" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">h</text> <rect x="145" y="98" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="163" y="115" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T3</text> <rect x="12" y="173" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="30" y="190" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T1</text> <rect x="82" y="173" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="100" y="190" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T2</text> <!-- Flecha --> <line x1="240" y1="120" x2="360" y2="120" stroke="#2563eb" stroke-width="2"/> <polygon points="360,120 348,114 348,126" fill="#2563eb"/> <text x="300" y="108" text-anchor="middle" font-size="12" fill="#2563eb">rotarDerecha</text> <!-- Árbol derecho: después --> <line x1="455" y1="50" x2="425" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="475" y1="50" x2="510" y2="100" stroke="#334155" stroke-width="1.5"/> <line x1="500" y1="125" x2="475" y2="175" stroke="#334155" stroke-width="1.5"/> <line x1="520" y1="125" x2="545" y2="175" stroke="#334155" stroke-width="1.5"/> <circle cx="465" cy="35" r="18" fill="#ecfdf5" stroke="#059669" stroke-width="1.5"/> <text x="465" y="35" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">h</text> <rect x="407" y="98" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="425" y="115" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T1</text> <circle cx="510" cy="115" r="18" fill="#eff6ff" stroke="#334155" stroke-width="1.5"/> <text x="510" y="115" text-anchor="middle" dominant-baseline="middle" font-size="13" fill="#0f172a">z</text> <rect x="457" y="173" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="475" y="190" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T2</text> <rect x="527" y="173" width="36" height="34" rx="4" fill="#f8fafc" stroke="#334155" stroke-width="1.5"/> <text x="545" y="190" text-anchor="middle" dominant-baseline="middle" font-size="12" fill="#0f172a">T3</text> </svg>

Java:

Nodo rotarDerecha(Nodo z) {
    Nodo h  = z.izq;     // hijo izquierdo, será la nueva raíz
    Nodo T2 = h.der;     // subárbol que cambia de padre

    // 1) Reasignación de punteros (orden seguro: primero "girar")
    h.der = z;           // z pasa a ser hijo derecho de h
    z.izq = T2;          // T2 pasa a ser hijo izquierdo de z

    // 2) Recalcular alturas: PRIMERO el nodo que queda abajo (z),
    //    LUEGO el que queda arriba (h), porque h depende de z.
    z.altura = 1 + Math.max(altura(z.izq), altura(z.der));
    h.altura = 1 + Math.max(altura(h.izq), altura(h.der));

    // 3) Devolver la nueva raíz del subárbol
    return h;
}

int altura(Nodo n) { return (n == null) ? -1 : n.altura; }

Punteros reasignados y orden:

  1. Guardar h = z.izq y T2 = h.der antes de modificar nada (si no, se pierden las referencias).
  2. h.der = z (z baja a la derecha de h).
  3. z.izq = T2 (T2 cuelga de z).
  4. Recalcular alturas de abajo arriba: primero z (queda como hijo) y después h (queda como nueva raíz y su altura depende de la ya actualizada de z).
  5. Retornar h para que el padre re-enlace el subárbol.

El coste es O(1) (número constante de reasignaciones).

Composición de la rotación LR (en una frase): el caso LR se resuelve con dos rotaciones simples encadenadas: primero una rotación a la izquierda sobre el hijo izquierdo del nodo desequilibrado (lo transforma en un caso LL) y, a continuación, una rotación a la derecha sobre el propio nodo desequilibrado. Simétricamente, RL = rotación derecha sobre el hijo derecho + rotación izquierda sobre el nodo.

Cuestión 5. Comparación con tabla hash

Criterio Árbol AVL Tabla hash
Búsqueda — caso medio O(log n) O(1)
Búsqueda — peor caso O(log n) (garantizado por el invariante) O(n) (todas las claves colisionan en el mismo cubo)
Inserción / borrado O(log n) O(1) medio / O(n) peor
Recorrido ordenado O(n) in-orden, sin coste extra No soportado; exige extraer todo y ordenar → O(n·log n)
Consulta por rango O(log n + k) Ineficiente: hay que examinar todos los cubos → O(n)
Memoria Punteros por nodo + altura Array + gestión de colisiones

(a) Complejidad de la búsqueda por clave.

  • AVL: O(log n) tanto en caso medio como en el peor caso, porque la altura está acotada logarítmicamente por el invariante de equilibrio.
  • Tabla hash: O(1) en el caso medio (con buena función hash y factor de carga controlado), pero O(n) en el peor caso, cuando todas las claves caen en el mismo cubo (colisiones masivas o función hash deficiente).

(b) Operaciones «gratis» del AVL. Al ser un árbol binario de búsqueda ordenado, el AVL ofrece sin coste adicional:

  • Recorrido ordenado (in-orden): visita las claves de menor a mayor id en O(n), directamente.
  • Consultas por rango (p. ej. «recursos con id entre 100 y 200»): O(log n + k), localizando el extremo inferior y avanzando in-orden.
  • Además: mínimo / máximo (O(log n)) y predecesor / sucesor de una clave.

Una tabla hash no preserva ningún orden entre las claves: el hashing dispersa los id por los cubos sin relación con su valor, de modo que recorrer ordenado o consultar rangos obliga a leer toda la tabla y reordenar.

(c) Factor de carga. El factor de carga es:

\lambda = \frac{n}{m} \qquad (n = \text{núm. de elementos almacenados},\ m = \text{núm. de cubos/buckets})

Mide cómo de «llena» está la tabla. Al crecer λ aumenta la probabilidad de colisión y, por tanto, la longitud media de las listas de desbordamiento (en encadenamiento) o el número de sondeos (en direccionamiento abierto), degradando el O(1) ideal. Para mantener el rendimiento se realiza rehashing (se amplía m y se reubican las claves) cuando λ supera un umbral típico (≈ 0,75 en encadenamiento; menor, ≈ 0,5, en direccionamiento abierto).

Conclusión. Si el requisito principal es listar los recursos ordenados por id (y previsiblemente hacer consultas por rango), la elección correcta es el árbol AVL: ofrece el recorrido ordenado y las consultas por rango de forma nativa y eficiente, con búsqueda/inserción/borrado garantizados en O(log n) incluso en el peor caso. La tabla hash sería preferible solo si la operación dominante fuera el acceso puntual por clave exacta sin necesidad de orden ni rangos, donde su O(1) medio resulta imbatible.

Practica con supuestos como este

Genera supuestos de tu especialidad y comunidad en cualquiera de los cuatro formatos, resuélvelos y recibe corrección con nota al instante.