39. Árboles como estructuras matemáticas

Los árboles son grafos conectados sin ciclos. Su estructura jerárquica permite organizar datos, representar decisiones, buscar valores, modelar sistemas de archivos y recorrer relaciones de dependencia.

39.1 Introducción

Los grafos describen conexiones generales. Cuando las conexiones forman una jerarquía sin ciclos, aparece una estructura especialmente útil: el árbol.

Los árboles están presentes en el sistema de archivos, el DOM de una página, los árboles de búsqueda, los menús, los compiladores y muchas representaciones de decisiones. Su ausencia de ciclos hace que entre dos nodos haya un camino inequívoco.

39.2 Definición de árbol

Un árbol es un grafo no dirigido, finito, conectado y sin ciclos. Un bosque es un grafo sin ciclos que puede tener varias componentes conectadas.

Árbol: todos los vértices se alcanzan entre sí y no existe ningún ciclo.

Bosque: no hay ciclos, pero puede haber varios árboles desconectados.

Agregar una arista entre dos vértices de un árbol crea exactamente un ciclo.

La conectividad garantiza que el árbol es una sola pieza. La aciclicidad evita rutas redundantes que vuelven al mismo lugar.

39.3 Propiedades equivalentes

Para un grafo no dirigido con n vértices, las siguientes condiciones son equivalentes cuando se cumplen los supuestos adecuados:

Es un árbol.
Es conectado y tiene n - 1 aristas.
No tiene ciclos y tiene n - 1 aristas.
Entre todo par de vértices existe un único camino simple.
Es conectado y quitar cualquier arista lo desconecta.

Estas caracterizaciones ofrecen distintas formas de reconocer un árbol. Según los datos disponibles, puede resultar más fácil contar aristas, buscar ciclos o comprobar conectividad.

39.4 Número de aristas

Un árbol con n vértices tiene exactamente n - 1 aristas. Esta es una de sus propiedades más útiles.

1 vértice → 0 aristas.
2 vértices → 1 arista.
5 vértices → 4 aristas.
n vértices → n - 1 aristas.

La propiedad puede demostrarse por inducción: al agregar una hoja a un árbol se agrega un vértice y exactamente una arista, preservando la diferencia de uno.

39.5 Caminos únicos

En un árbol existe un único camino simple entre cada par de vértices. Si hubiera dos caminos simples distintos entre los mismos vértices, al combinarlos se formaría un ciclo.

En una jerarquía de carpetas hay una única cadena de ancestros desde un archivo hasta la raíz.

En un árbol de decisiones, cada entrada sigue una única ruta desde la raíz hasta una hoja.

Esta unicidad simplifica búsquedas y razonamientos. En un grafo general pueden existir muchas rutas alternativas, lo que requiere decidir cuál recorrer o cuál es mejor.

39.6 Árboles enraizados

Un árbol enraizado elige un vértice especial llamado raíz. Al orientarlo conceptualmente desde la raíz hacia abajo, aparecen relaciones de padre, hijo, ancestro y descendiente.

Raíz: nodo sin padre.
Padre: vecino inmediatamente anterior hacia la raíz.
Hijo: vecino inmediatamente posterior.
Ancestro: cualquier nodo en el camino a la raíz.
Descendiente: cualquier nodo debajo de otro.

La raíz no cambia las aristas del árbol original; agrega una perspectiva jerárquica. Elegir otra raíz puede cambiar quién es padre o hijo sin cambiar el grafo subyacente.

39.7 Hojas, nodos internos y hermanos

En un árbol enraizado, una hoja es un nodo sin hijos. Un nodo interno tiene al menos un hijo. Dos nodos con el mismo padre son hermanos.

Directorio raíz
├── documentos
│ ├── informe.pdf
│ └── notas.txt
└── imágenes

informe.pdf y notas.txt son hojas y hermanos.

El término hoja puede tener una definición distinta en un árbol no enraizado, donde suele referirse a un vértice de grado 1. Es importante indicar qué convención se usa.

39.8 Profundidad, altura y nivel

La profundidad de un nodo es el número de aristas desde la raíz hasta ese nodo. La altura de un nodo es la longitud del camino más largo desde él hasta una hoja. La altura del árbol es la altura de la raíz.

Raíz: profundidad 0.
Hijo de la raíz: profundidad 1.

Si el camino más largo raíz-hoja tiene 3 aristas,
la altura del árbol es 3.

Algunos textos cuentan niveles desde 1 o miden altura por nodos en vez de aristas. En un algoritmo o documentación conviene fijar la convención para evitar errores de uno.

39.9 Árboles ordenados

En un árbol ordenado, el orden entre los hijos de cada nodo es relevante. Por ejemplo, un primer y un segundo hijo no son intercambiables.

Árbol no ordenado: hijos {A, B}.
Árbol ordenado: hijo izquierdo A e hijo derecho B.

Intercambiar A y B cambia el árbol ordenado.

Los árboles de sintaxis y los árboles binarios suelen ser ordenados. En cambio, una jerarquía de categorías puede tratar sus hijos como un conjunto sin orden intrínseco.

39.10 Árboles binarios

Un árbol binario es un árbol enraizado y ordenado en el que cada nodo tiene como máximo dos hijos: izquierdo y derecho.

Nodo
├── hijo izquierdo
└── hijo derecho

Cada posición puede estar vacía o contener otro subárbol binario.

La distinción entre izquierdo y derecho es parte de la estructura. Un nodo con solo hijo izquierdo no es igual, como árbol ordenado, a un nodo con solo hijo derecho.

39.11 Árbol binario completo, lleno y perfecto

Estas expresiones se parecen, pero describen propiedades distintas:

TipoPropiedad
Lleno o estrictoTodo nodo interno tiene exactamente dos hijos.
CompletoTodos los niveles se llenan salvo quizá el último, que se llena de izquierda a derecha.
PerfectoTodo nodo interno tiene dos hijos y todas las hojas están a la misma profundidad.

Todo árbol perfecto es lleno y completo, pero las otras implicaciones no se cumplen en general. La terminología puede variar ligeramente entre fuentes, por lo que conviene describir la propiedad concreta.

39.12 Capacidad de un árbol binario

Si la raíz tiene altura 0, un árbol binario de altura h tiene como máximo 2h+1 - 1 nodos. En el nivel i hay como máximo 2i nodos.

Nivel 0: hasta 1 nodo.
Nivel 1: hasta 2 nodos.
Nivel 2: hasta 4 nodos.

Hasta altura 2: 1 + 2 + 4 = 7 nodos.

La relación inversa muestra que un árbol binario equilibrado con n nodos tiene altura proporcional a log2(n), una razón clave de su eficiencia en búsquedas.

39.13 Representar un árbol con nodos

class NodoBinario {
  constructor(valor, izquierdo = null, derecho = null) {
    this.valor = valor;
    this.izquierdo = izquierdo;
    this.derecho = derecho;
  }
}

const arbol = new NodoBinario(
  8,
  new NodoBinario(3, new NodoBinario(1), new NodoBinario(6)),
  new NodoBinario(10)
);

Cada nodo conserva referencias a sus hijos. La ausencia de hijo se representa con null. Esta estructura refleja directamente la definición recursiva de un árbol binario.

39.14 Recorrido preorden

Un recorrido visita todos los nodos siguiendo una regla. En preorden se procesa primero el nodo actual, después el subárbol izquierdo y finalmente el derecho.

Preorden(nodo):
1. Visitar nodo.
2. Recorrer izquierdo.
3. Recorrer derecho.

Para el árbol del ejemplo: 8, 3, 1, 6, 10.

El preorden es útil para serializar una estructura jerárquica o procesar un contenedor antes de sus contenidos.

39.15 Recorridos inorden y postorden

En inorden se recorre izquierdo, nodo y derecho. En postorden se recorre izquierdo, derecho y nodo.

Inorden del ejemplo: 1, 3, 6, 8, 10.
Postorden del ejemplo: 1, 6, 3, 10, 8.

En un árbol binario de búsqueda, inorden devuelve los valores ordenados.

El postorden es apropiado cuando un nodo debe procesarse después de todos sus descendientes, como al calcular tamaños acumulados o liberar estructuras.

39.16 Implementar recorridos recursivos

function preorden(nodo, resultado = []) {
  if (nodo === null) return resultado;
  resultado.push(nodo.valor);
  preorden(nodo.izquierdo, resultado);
  preorden(nodo.derecho, resultado);
  return resultado;
}

function inorden(nodo, resultado = []) {
  if (nodo === null) return resultado;
  inorden(nodo.izquierdo, resultado);
  resultado.push(nodo.valor);
  inorden(nodo.derecho, resultado);
  return resultado;
}

console.log(preorden(arbol)); // [8, 3, 1, 6, 10]
console.log(inorden(arbol));  // [1, 3, 6, 8, 10]

La recursión encaja naturalmente porque cada hijo es a su vez la raíz de un subárbol. En árboles muy profundos puede ser preferible una implementación iterativa con una pila explícita.

39.17 Recorrido por niveles

El recorrido por niveles, también llamado en anchura, visita primero la raíz, luego todos sus hijos, después los nietos y así sucesivamente. Usa una cola para recordar los nodos pendientes.

Para el árbol del ejemplo:
8, 3, 10, 1, 6.

Es útil para procesar niveles, calcular distancias desde la raíz y serializar árboles completos.

Este recorrido anticipa la búsqueda en anchura de grafos. La diferencia es que en un árbol no hay ciclos que obliguen a marcar vértices ya visitados.

39.18 Árbol binario de búsqueda

Un árbol binario de búsqueda o BST organiza valores comparables con una regla: los valores del subárbol izquierdo son menores que el nodo y los del derecho son mayores, según la política elegida para duplicados.

Para el nodo 8:
izquierdo: 3, 1 y 6, todos menores que 8.
derecho: 10, mayor que 8.

Inorden produce: 1, 3, 6, 8, 10.

Buscar, insertar y eliminar pueden ser O(log n) si el árbol está equilibrado. Si se insertan valores ordenados en un BST simple, puede degenerar en una cadena y las operaciones pasan a O(n).

39.19 Heaps o montículos

Un heap binario es un árbol binario completo con una propiedad de prioridad. En un min-heap, cada nodo es menor o igual que sus hijos; por ello, el mínimo está en la raíz.

Min-heap:
raíz ≤ hijos.
La relación no ordena por completo los hermanos ni todos los descendientes.

Se suele almacenar eficientemente en un arreglo.

Los heaps implementan colas de prioridad y se usan en planificación de tareas y algoritmos de caminos mínimos. No deben confundirse con un BST: sus reglas de orden son diferentes.

39.20 Árboles en aplicaciones

  • Sistemas de archivos y jerarquías de directorios.
  • DOM de documentos HTML y componentes de interfaces.
  • Índices de bases de datos y estructuras de búsqueda.
  • Tries para prefijos, autocompletado y diccionarios.
  • Árboles de decisión, sintaxis y expresiones de un compilador.
  • Colas de prioridad basadas en heaps.

Un árbol es adecuado cuando cada elemento, salvo la raíz, tiene un único padre en la jerarquía. Si un elemento debe tener varios padres, el modelo general de grafo suele ser más apropiado.

39.21 Errores frecuentes

  • Creer que cualquier estructura dibujada en niveles es un árbol aunque tenga ciclos o vértices desconectados.
  • Olvidar que un árbol de n vértices tiene n - 1 aristas.
  • Confundir profundidad con altura o cambiar la convención de conteo sin indicarlo.
  • Confundir árbol binario completo, lleno y perfecto.
  • Suponer que un BST siempre es eficiente sin considerar su equilibrio.
  • Usar recorridos recursivos profundos sin contemplar el límite de la pila de llamadas.

39.22 Qué debes recordar y conclusión

  • Un árbol es un grafo conectado y sin ciclos.
  • Con n vértices tiene exactamente n - 1 aristas y un camino simple único entre cada par.
  • Una raíz define relaciones de padre, hijo, ancestro, hoja, profundidad y altura.
  • Los árboles binarios tienen hasta dos hijos ordenados por nodo.
  • Los recorridos preorden, inorden, postorden y por niveles procesan la estructura de maneras distintas.
  • BST, heaps, tries y jerarquías de archivos son aplicaciones de árboles.

Los árboles organizan relaciones jerárquicas de forma eficiente y sin ambigüedad de caminos. En el próximo tema ampliaremos la mirada sobre grafos para estudiar caminos, ciclos y conectividad.