Uso de Algoritmos Recursivos para Recorrer Estructuras de Árbol

La recursión es una de las herramientas más elegantes de la programación: una función que se llama a sí misma para resolver instancias progresivamente más pequeñas de un problema. Para recorrer estructuras de árbol — como el sistema de archivos, árboles de categorías o estructuras XML — suele ser la solución más natural y eficiente. Este artículo explica el concepto y lo ilustra con un ejemplo concreto en C#.

Cómo funciona la recursión

La idea subyacente es simple: una función recibe un nodo como parámetro, lo procesa y se llama a sí misma tantas veces como ramas hijas tenga el nodo actual. Las llamadas sucesivas terminan cuando se alcanzan los nodos “hoja” — aquellos sin hijos.

Sin una condición de salida bien definida, la recursión genera un bucle infinito que conduce a un desbordamiento de pila. La función debe, por tanto, llamarse a sí misma únicamente si aún hay datos que procesar.

Ventajas y limitaciones

  • Principal ventaja: código compacto y expresivo para navegar por estructuras jerárquicas de profundidad arbitraria.
  • Limitación 1: sin una gestión correcta de la condición de salida, genera bucles infinitos.
  • Limitación 2: cada llamada añade un marco a la pila. En estructuras muy profundas, el desbordamiento de pila es posible incluso con una lógica correcta.
  • Buena práctica: minimizar los datos pasados como parámetros — es preferible pasar referencias o índices antes que copiar objetos enteros.

Ejemplo: exploración recursiva del sistema de archivos en C#

El ejemplo implementa un recorrido recursivo de carpetas del sistema de archivos. La función ReadFolderFiles recibe una carpeta, la recorre y, por cada subcarpeta encontrada, se llama a sí misma de nuevo con la subcarpeta como nuevo parámetro. El algoritmo termina cuando no quedan más subcarpetas por visitar.

using System;
using System.IO;

namespace RecursionOnFS
{
    class Class1
    {
        [STAThread]
        static void Main(string[] args)
        {
            DirectoryInfo d = new DirectoryInfo(@"C:StartingDirectory");
            ReadFolderFiles(d);
        }

        static void ReadFolderFiles(DirectoryInfo pDir)
        {
            DirectoryInfo[] subd = pDir.GetDirectories();
            foreach (DirectoryInfo dd in subd)
            {
                if (dd.Attributes == FileAttributes.Directory)
                {
                    // Recursive call: descend into the subfolder
                    ReadFolderFiles(dd);
                }
                else
                {
                    Console.WriteLine(dd.FullName);
                }
            }
        }
    }
}

El mismo patrón se aplica a cualquier estructura jerárquica: árboles de categorías en una base de datos, nodos XML, estructuras JSON anidadas o grafos acíclicos. La clave es siempre la misma — identificar la condición de salida y reducir el problema en cada iteración.

Artículo publicado originalmente en itvirtualcommunity.net (2004), reescrito y actualizado en 2012.