BSTs en C (y parte 3)

Implementar la operación Eliminar Nodo no es fácil, pero había que hacerlo, ¿no? Nos hará falta implementar otras operaciones auxiliares: ObtenerNodo, ObtenerMinimo, Reemplazar...

Cuando trabajamos con árboles binarios de búsqueda en C, una de las operaciones más complejas pero fundamentales es la eliminación de nodos. Para abordar esta tarea con eficacia, es necesario considerar los distintos casos que pueden presentarse: eliminar una hoja, eliminar un nodo con un solo hijo o eliminar un nodo con dos hijos. Para facilitar estas operaciones, una mejora clave es modificar la estructura del nodo para que cada uno tenga una referencia a su padre. Esto nos permite manejar las conexiones entre nodos de forma más sencilla y clara.

Empezamos por añadir un campo padre en la estructura del nodo y adaptamos la función que crea nodos para que reciba también el nodo padre. Esto implica modificar la función de inserción para que, además del árbol y el valor a insertar, reciba el padre del nodo actual. Para no complicar la interfaz pública, creamos una función estática interna que maneja la inserción con el padre, y una función pública que inicia la inserción pasando NULL como padre para la raíz.

Para comprobar que la referencia al padre funciona correctamente, podemos modificar la función de recorrido inorden para que imprima, además del valor del nodo, el valor de su padre cuando exista. Así confirmamos que cada nodo conoce a su padre, lo que será fundamental para la eliminación.

Antes de implementar la eliminación, definimos algunas funciones de apoyo. La primera es obtener, que busca un nodo con un valor dado dentro del árbol. Esta función es recursiva y sigue la lógica del árbol binario de búsqueda: si el árbol está vacío, devuelve NULL; si el valor coincide con el nodo actual, devuelve ese nodo; si el valor es menor, busca en el subárbol izquierdo; si es mayor, en el derecho.

Otra función importante es reemplazar, que nos permite cambiar un nodo por otro en la estructura del árbol. Esta función recibe el nodo a reemplazar y el nuevo nodo que ocupará su lugar. Primero verifica que el nodo a reemplazar tenga padre; si es así, determina si es hijo izquierdo o derecho y actualiza el puntero correspondiente del padre para que apunte al nuevo nodo. Además, si el nuevo nodo no es NULL, actualiza su referencia al padre para mantener la conexión bidireccional.

Con estas herramientas, podemos abordar la función principal de eliminación. La función eliminar busca el nodo con el valor a eliminar usando la función obtener. Si el nodo no existe o el árbol está vacío, no hace nada. Cuando encuentra el nodo, llama a una función auxiliar eliminarNodo que maneja los distintos casos.

Si el nodo a eliminar es una hoja (sin hijos), simplemente usamos reemplazar para que el padre deje de apuntar a él, pasando NULL como nuevo nodo, y luego liberamos la memoria del nodo eliminado.

Si el nodo tiene un solo hijo, ya sea izquierdo o derecho, usamos reemplazar para que el padre del nodo eliminado apunte directamente a ese hijo, y luego liberamos el nodo eliminado. Esto mantiene la estructura del árbol sin perder nodos.

El caso más complejo es cuando el nodo a eliminar tiene dos hijos. Para resolverlo, buscamos el nodo mínimo del subárbol derecho, que será el sucesor en orden del nodo a eliminar. Implementamos una función minimo que, dado un árbol, devuelve el nodo con el valor mínimo recorriendo recursivamente el hijo izquierdo hasta que no haya más. Una vez obtenido este nodo mínimo, copiamos su valor al nodo que queremos eliminar y luego eliminamos el nodo mínimo del subárbol derecho, que ahora está duplicado. Esto mantiene la propiedad del árbol binario de búsqueda.

Para ilustrar, si eliminamos la raíz que tiene dos hijos, el valor de la raíz se reemplaza por el mínimo del subárbol derecho, y luego eliminamos ese nodo mínimo, que será un caso más sencillo (hoja o con un solo hijo).

Con estas funciones y modificaciones, tenemos una implementación completa y robusta para eliminar nodos en árboles binarios de búsqueda en C, manejando todos los casos posibles y manteniendo la integridad del árbol. Además, al encapsular las funciones auxiliares como estáticas, limitamos su visibilidad al archivo fuente, manteniendo el código limpio y modular.

Lista de reproducción
  1. 1
    Qué son las estructuras de datos
    3 minutos
  2. 2
    Listas enlazadas: Teoría
    12 minutos
  3. 3
    Listas enlazadas en C
    18 minutos
  4. 4
    Listas enlazadas en Java
    17 minutos
  5. 5
    Colas: teoría
    6 minutos
  6. 6
    Colas en C
    12 minutos
  7. 7
    Colas en Java
    10 minutos
  8. 8
    Pilas: teoría
    7 minutos
  9. 9
    Pilas en C
    11 minutos
  10. 10
    Pilas en Java
    10 minutos
  11. 11
    Introducción a los árboles
    11 minutos
  12. 12
    Árboles binarios
    9 minutos
  13. 13
    Árboles binarios de búsqueda (parte 1)
    10 minutos
  14. 14
    Árboles de búsqueda binaria (parte 2)
    8 minutos
  15. 15
    BSTs en C (parte 1)
    13 minutos
  16. 16
    BSTs en C (parte 2)
    8 minutos
  17. 17
    BSTs en C (y parte 3)
    18 minutos
  18. 18
    BSTs en Java (parte 1)
    9 minutos
  19. 19
    BSTs en Java (parte 2)
    11 minutos
  20. 20
    BSTs en Java (parte 3)
    10 minutos
  21. 21
    Árbol desde preorden e inorden
    8 minutos
  22. 22
    Árbol desde inorden y postorden
    8 minutos
  23. 23
    Árboles k-arios
    6 minutos
  24. 24
    Árboles B (parte 1)
    11 minutos
  25. 25
    Árboles B (parte 2)
    20 minutos
  26. 26
    Árboles B (parte 3)
    39 minutos